ExpandCollapse

+ 1 Driver flx_run

+ 1.1 Entry points

This header specifies the interface for two entry points, felix_run and felix_arun. The first provides a driver function that refuses to support asynchronous I/O, and is suitable for embedded systems. The second provides asynchronous I/O support which includes support for real time clock and sockets.

Only one of these entry points will actually be defined in a given translation unit.

share/lib/rtl/flx_run.hpp

int felix_run(int, char**);
int felix_arun(int, char**);

+ 1.2 Implementation

This file contains FOUR separate sets of four callback functions and a mainline.

It is designed to be included in four stub files which set the four combinations, so common code can be shared.

These are conditioned by two boolean macros:

FLX_BUILD_FOR_STATIC_LINK: if defined, we're static linking if not defined, we're dynamic linking

FLX_SUPPORT_ASYNC: if defined 0, async support is not provided if defined non-zero, async support is provided this macro must be defined

In addition we notice these macros too:

FLX_WIN32: if defined non-zero, we're running Win32

FLX_HAVE_MSVC: if defined non-zero we're using MSVC++ compiler and SDK used to decide the name of the async library dll

NOTE: The macro "FLX_STATIC_LINK" will ALSO be defined by the toolchain. This is UNRELATED to the FLX_BUILD_FOR_STATIC_LINK macro. The FLX_STATIC_LINK macro says that all unresolved externals linking the flx_(a)_run executables are to be found in libraries statically. These executables ALWAYS dynamically load Felix DLLs using dlopen/LoadLibrary.

But the exes themselves are fully statically linked (except for C/C++ standard libraries of course). The flx_(a)run exes are univeral drivers. To make them the macro FLX_BUILD_FOR_STATIC_LINK must be *undefined*.

The same source code is ALSO used to statically link your program into an executable. In this case again, all the object files have to be FLX_STATIC_LINK however this time we get code produced with FLX_BUILD_FOR_STATIC link defined.

Note that a flx_run that satisfies its externals from a DLL would also be possible but we don't build one of them. That would be PATH dependent, and the PATH might be *different* to the one the client DLL program requires.

share/src/flx_drivers/flx_run.include

#include <cstdlib>
#include <stdio.h>
#include <string.h>

#include <string>

#include "flx_world.hpp"
#include "flx_async_world.hpp"
#include "flx_ts_collector.hpp"
#include "flx_eh.hpp"

using namespace std;
using namespace flx::rtl;
using namespace flx::run;

// non async drivers don't depend on faio<-demux<-winsock
// and so aren't linked with mswsock and ws2_32
// Cygwin doesn't use windows sockets either
#if !FLX_CYGWIN && FLX_WIN32 && FLX_SUPPORT_ASYNC
  #include "demux_iocp_demuxer.hpp"
  // needed to perform win socket io (calls WSAInit). Must happen
  // before iocp_demuxer is instantiated and (I assume) happen
  // only once.
  // JS: No, it can be called any number of times, provided
  // the destructor WSACleanup is called same number of times
  // Use of this RAII object ensures WSAinit/Cleanup calls balance.
  // RF: Still has to happen before any socket calls. Putting it in
  // the async object which is created on demand is already too late.
  // If that's a problem then any socket creation calls would have to
  // gratuitously make async calls.
  flx::demux::winsock_initer wsinit;
#endif

// Actually on Cygwin it might be cygflx_async_dynamic .. not sure
#if !FLX_CYGWIN && FLX_HAVE_MSVC
   #define FLX_ASYNC_DLL_NAME "flx_async_dynamic"
#else
   #define FLX_ASYNC_DLL_NAME "libflx_async_dynamic"
#endif

#ifdef FLX_BUILD_FOR_STATIC_LINK
extern "C" void *flx_main;
extern void *static_create_thread_frame;
extern void *static_flx_start;
#endif

namespace flx { namespace run {

+ 1.3 init_ptr_create_async_hooker callback #1

CALLBACK #1 init_ptr_create_async_hooker

This is a really ugly piece of hackery!

General Felix provides async I/O which is loaded and initialised on demand, i.e. on the first use.

This is done so programs not doing socket or timer I/O don't spawn an extra thread, and programs which do do not spawn it prematurely.

Therefore the asynchronous I/O subsystem is initially represented by a NULL pointer. When its services are required, the shared library providing them is dynamically loaded by name, and the service started.

However if static linkage is being used, the code is linked in statically instead. In this case, the load step can be skipped, but the service must still be started on demand.

Furthermore, Felix provides two drivers, flx_run and flx_arun. The former driver does not permit any asynchronous I/O. This is useful on a platform where we cannot provide these services, and it's also useful if we want to physically guarantee that such services cannot be run.

We represent these options by using two pointers. One pointer contains a function will initialises the other. The first pointer represents the service creator, and the second the actual service.

If the creator is NULL, the service can never be started. This is the variable ptr_create_async_hooker in the config. It is set to zero if async support is disabled by conditional compilation of this driver code, used to produce flx_run, the restricted version of Felix.

If async is to be supported, then if we're static linking we set the pointer to the service initialiser create_async_hooker which has to have been statically linked in.

If we're dynamic linking, we load the shared library FLX_ASYNC_DLL_NAME dynamically, and use dlsym() or GetProcAddress() to fetch the service creator function from its string name.

share/src/flx_drivers/flx_run.include

void init_ptr_create_async_hooker(flx_config *c, bool debug_driver) {
#if !FLX_SUPPORT_ASYNC
  if(debug_driver)
    fprintf(stderr,"[flx_run.include]: FLX_SUPPORT_ASYNC FALSE\n");
  c->ptr_create_async_hooker = 0;
#else
  c->ptr_create_async_hooker = create_async_hooker;
  if(debug_driver)
    fprintf(stderr,"[flx_run.include]: FLX_SUPPORT_ASYNC TRUE, create_async_hooker = %p\n", create_async_hooker);
#ifndef FLX_BUILD_FOR_STATIC_LINK
  // Try to dynamically load the felix asynchronous library

  if(debug_driver)
    fprintf(stderr,"[flx_run.include]: dymamic_link: trying to load %s\n",FLX_ASYNC_DLL_NAME);

  FLX_LIBHANDLE async_lib = ::flx::dynlink::flx_load_module_nothrow(FLX_ASYNC_DLL_NAME);

  // Error out if we couldn't load the library.
  if (async_lib == FLX_NOLIBRARY) {
    fprintf(stderr,
      "[flx_run.include]: dynamic_link: Unable to find module '%s'\n",FLX_ASYNC_DLL_NAME);
    exit(1);
  }
  // debug only ..
  else {
    if (debug_driver)
      fprintf(stderr, "[flx_run.include]: dynamic_link: module '%s' loaded!\n",FLX_ASYNC_DLL_NAME);
  }

  // Get the hooker function
  c->ptr_create_async_hooker =
    (create_async_hooker_t*)FLX_DLSYM(async_lib, create_async_hooker);

  // Error out if we couldn't find the hooker function in the
  // library.
  if (c->ptr_create_async_hooker == NULL) {
    fprintf(stderr,
      "[flx_run.include]: dynamic_link: Unable to find symbol 'create_async_hooker' in module "
      "'%s'\n",FLX_ASYNC_DLL_NAME);
    exit(1);
  }
  // debug only
  else {
    if (debug_driver)
      fprintf(stderr, "[flx_run.include]: dynamic_link: found 'create_async_hooker'!\n");
  }
#else
  if(debug_driver)
    fprintf(stderr,"[flx_run.include]: static_link: 'create_async_hooker' SHOULD BE LINKED IN\n");
#endif
#endif
}

+ 1.4 get_flx_args_config callback

CALLBACK #2: get_flx_args_config #2

Purpose: grabs program arguments. Prints help if statically linked.

Static and dynamic linked programs have arguments in different slots of argv because the mainline for dynamic linkage is actually flx_run executable whereas for static linkage this is the executable.

So dynamic linked programs have an extra argument which has to be skipped for compatibility of static and dynamic linkage.

share/src/flx_drivers/flx_run.include

int get_flx_args_config(int argc, char **argv, flx_config *c) {
#ifndef FLX_BUILD_FOR_STATIC_LINK
  c->static_link = false;
  if (argc<2)
  {
    printf("usage: flx_run [--debug] dll_filename options ..\n");
    printf("  environment variables (numbers can be decimals):\n");
    printf("  FLX_DEBUG               # enable debugging traces (default off)\n");
    printf("  FLX_DEBUG_ALLOCATIONS   # enable debugging allocator (default FLX_DEBUG)\n");
    printf("  FLX_DEBUG_COLLECTIONS   # enable debugging collector (default FLX_DEBUG)\n");
    printf("  FLX_REPORT_COLLECTIONS  # report collections (default FLX_DEBUG)\n");
    printf("  FLX_DEBUG_THREADS       # enable debugging collector (default FLX_DEBUG)\n");
    printf("  FLX_DEBUG_DRIVER        # enable debugging driver (default FLX_DEBUG)\n");
    printf("  FLX_FINALISE            # whether to cleanup on termination (default NO)\n");
    printf("  FLX_GC_FREQ=n           # how often to call garbage collector (default 1000)\n");
    printf("  FLX_MIN_MEM=n           # initial memory pool n Meg (default 10)\n");
    printf("  FLX_MAX_MEM=n           # maximum memory n Meg (default -1 = infinite)\n");
    printf("  FLX_FREE_FACTOR=n.m     # reset FLX_MIN_MEM to actual usage by n.m after gc (default 1.1) \n");
    printf("  FLX_ALLOW_COLLECTION_ANYWHERE # (default yes)\n");
    return 1;
  }
  c->filename = argv[1];
  c->flx_argv = argv+1;
  c->flx_argc = argc-1;
  c->debug = (argc > 1) && (strcmp(argv[1], "--debug")==0);
  if (c->debug)
  {
    if (argc < 3)
    {
      printf("usage: flx_run [--debug] dll_filename options ..\n");
      return 1;
    }
    c->filename = argv[2];
    --c->flx_argc;
    ++c->flx_argv;
  }
#else
  c->static_link = true;
  c->filename = argv[0];
  c->flx_argv = argv;
  c->flx_argc = argc;
  c->debug = false;

//  printf("Statically linked Felix program running\n");
#endif
  return 0;
}

+ 1.4.1 The modulenameoffilename helper function.

A helper routine for finding the module name when static linking.

Static link executables get their full pathname in argv[0]. This has to be parsed to get the module name which is then set into the library linkage object.

For dynamic link programs the library name is passed to the library linkage loader function, which does the parsing itself.

This is a hack. It should be done in the library linkage class.

share/src/flx_drivers/flx_run.include

#ifdef FLX_BUILD_FOR_STATIC_LINK
static ::std::string modulenameoffilename(::std::string const &s)
{
  ::std::size_t i = s.find_last_of("\\/");
  ::std::size_t j = s.find_first_of(".",i+1);
  return s.substr (i+1,j-i-1);
}
#endif


+ 1.6 Mainline

share/src/flx_drivers/flx_run.include

int FELIX_MAIN (int argc, char** argv)
{
//fprintf(stderr,"felix_run=FELIX_MAIN starts\n");
  int error_exit_code = 0;
  flx_config *c = new flx_config(link_library, init_ptr_create_async_hooker, get_flx_args_config);
// WINDOWS CRASHES HERE (the constructor runs)
//fprintf(stderr,"flx_config created\n");
  flx_world *world=new flx_world(c);
//fprintf(stderr,"flx_world created\n");
  try {

    error_exit_code = world->setup(argc, argv);

    if(0 != error_exit_code) return error_exit_code;

  // MAINLINE, ONLY DONE ONCE
  // TODO: simply return error_exit_code
    // We're all set up, so run felix
    world->begin_flx_code();

    // Run the felix usercode.
    error_exit_code = world->run_until_complete();
    if(0 != error_exit_code) return error_exit_code;

    world->end_flx_code();

    error_exit_code = world->teardown();
  }
  catch (flx_exception_t &x) { error_exit_code = flx_exception_handler(&x); }
  catch (std::exception &x) { error_exit_code = std_exception_handler (&x); }
  catch (std::string &s) { error_exit_code = 6; fprintf(stderr, "%s\n", s.c_str()); }
  catch (...)
  {
    fprintf(stderr, "flx_run driver ends with unknown EXCEPTION\n");
    error_exit_code = 4;
  }
  delete world;
  delete c;

  return error_exit_code;
}


+ 1.11 Traditional Mainline with async support

Link this, together with translation units containing flx_arun, to create a static link executable with async support.

share/src/flx_drivers/flx_arun_main.cxx

#include "flx_run.hpp"

// to set the critical error handler
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#endif

int main(int argc, char **argv) 
{
  #ifdef _WIN32
  SetErrorMode (SEM_FAILCRITICALERRORS);
  #endif
  return felix_arun(argc, argv);
}

+ 1.12 Traditional Mainline without async support

Link this, together with translation units containing flx_run, to create a static link executable without async support.

share/src/flx_drivers/flx_run_main.cxx

#include "flx_run.hpp"
#include "stdio.h"

// to set the critical error handler
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#endif

int main(int argc, char **argv) 
{
  #ifdef _WIN32
  SetErrorMode (SEM_FAILCRITICALERRORS);
  #endif
  //fprintf(stderr,"Felix mainline flx_run_main starts!\n");
  return felix_run(argc, argv);
}

+ 2 Driver executable config

$PWD/src/config/flx_arun.fpc

Name: flx_arun
Description: Felix standard driver, async support
Requires: flx_async faio demux flx_pthread flx flx_gc flx_dynlink flx_strutil
flx_requires_driver: flx_arun
srcdir: src/flx_drivers
src: flx_arun_lib\.cpp|flx_arun_main\.cxx

$PWD/src/config/flx_run.fpc

Name: flx_run
Description: Felix standard driver, no async support
Requires: flx_pthread flx flx_gc flx_dynlink flx_strutil
srcdir: src/flx_drivers
src: flx_run_lib\.cpp|flx_run_main\.cxx

$PWD/src/config/flx_thread_free_run.fpc

Name: flx_thread_free_run
Description: Felix driver, no thread or async support
Description: WORK IN PROGRESS
Requires: flx flx_gc dl
srcdir: src/flx_drivers
src: flx_run_lib\.cpp|flx_run_main\.cxx

+ 3 Build Code

$PWD/buildsystem/flx_drivers.py

import fbuild
from fbuild.functools import call
from fbuild.path import Path
from fbuild.record import Record
import buildsystem
from buildsystem.config import config_call

# ------------------------------------------------------------------------------

def build( phase):
    #print("[fbuild:flx_drivers.py:build (in src/packages/driver.fdoc)] ********** BUILDING DRIVERS ***********************************************")
    path = Path(phase.ctx.buildroot/'share'/'src/flx_drivers')

    #dlfcn_h = config_call('fbuild.config.c.posix.dlfcn_h',
    #    phase.platform,
    #    phase.cxx.static,
    #    phase.cxx.shared)

    #if dlfcn_h.dlopen:
    #    external_libs = dlfcn_h.external_libs
    #    print("HAVE dlfcn.h, library=" + str (external_libs))
    #else:
    #    print("NO dlfcn.h available")
    #    external_libs = []
    external_libs = []

    run_includes = [
        phase.ctx.buildroot / 'host/lib/rtl',
        phase.ctx.buildroot / 'share/lib/rtl'
    ]

    arun_includes = run_includes + [
        'src/demux',
    ] + ([], ['src/demux/win'])['win32' in phase.platform]

    # Make four object files for flx_run 
    # two for async, two without
    # each pair made static and non static

    flx_run_static_static_obj = phase.cxx.static.compile(
        dst='host/lib/rtl/flx_run_lib_static',
        src=path / 'flx_run_lib_static.cpp',
        includes=run_includes,
        macros=['FLX_STATIC_LINK'],
    )

    flx_run_static_dynamic_obj = phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_run_lib_static',
        src=path / 'flx_run_lib_static.cpp',
        includes=run_includes,
    )


    flx_run_dynamic_dynamic_obj = phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_run_lib_dynamic',
        src=path / 'flx_run_lib_dynamic.cpp',
        includes=run_includes,
    )


    flx_arun_static_static_obj = phase.cxx.static.compile(
        dst='host/lib/rtl/flx_arun_lib_static',
        src=path / 'flx_arun_lib_static.cpp',
        includes=arun_includes,
        macros=['FLX_STATIC_LINK'],
    )

    flx_arun_static_dynamic_obj = phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_arun_lib_static',
        src=path / 'flx_arun_lib_static.cpp',
        includes=arun_includes,
    )


    flx_arun_dynamic_dynamic_obj = phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_arun_lib_dynamic',
        src=path / 'flx_arun_lib_dynamic.cpp',
        includes=arun_includes,
    )


    # Now, the mainline object files for static links
    flx_run_main_static= phase.cxx.static.compile(
        dst='host/lib/rtl/flx_run_main',
        src=path / 'flx_run_main.cxx',
        includes=run_includes,
        macros=['FLX_STATIC_LINK'],
    )

    flx_arun_main_static= phase.cxx.static.compile(
        dst='host/lib/rtl/flx_arun_main',
        src=path / 'flx_arun_main.cxx',
        includes=arun_includes,
        macros=['FLX_STATIC_LINK'],
    )

    # Now, the mainline object files for dynamic links
    flx_run_main_dynamic= phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_run_main',
        src=path / 'flx_run_main.cxx',
        includes=run_includes,
    )

    flx_arun_main_dynamic= phase.cxx.shared.compile(
        dst='host/lib/rtl/flx_arun_main',
        src=path / 'flx_arun_main.cxx',
        includes=arun_includes,
    )


    # And then the mainline executable for dynamic links
    flx_run_exe = phase.cxx.shared.build_exe(
        dst='host/bin/flx_run',
        srcs=[path / 'flx_run_main.cxx', path / 'flx_run_lib_dynamic.cpp'],
        includes=run_includes,
        external_libs=external_libs,
        libs=[call('buildsystem.flx_rtl.build_runtime',  phase).shared],
    )

    flx_arun_exe = phase.cxx.shared.build_exe(
        dst='host/bin/flx_arun',
        srcs=[path / 'flx_arun_main.cxx', path/ 'flx_arun_lib_dynamic.cpp'],
        includes=arun_includes,
        external_libs=external_libs,
        libs=[
           call('buildsystem.flx_rtl.build_runtime',  phase).shared,
           call('buildsystem.flx_pthread.build_runtime', phase).shared,
           call('buildsystem.flx_async.build_runtime', phase).shared,
           call('buildsystem.demux.build_runtime', phase).shared,
           call('buildsystem.faio.build_runtime', phase).shared],
    )

    return Record(
        flx_run_lib_static_static=flx_run_static_static_obj,
        flx_run_lib_static_dynamic=flx_run_static_dynamic_obj,
        flx_run_lib_dynamic_dynamic=flx_run_dynamic_dynamic_obj,
        flx_arun_lib_static_static=flx_arun_static_static_obj,
        flx_arun_lib_static_dynamic=flx_arun_static_dynamic_obj,
        flx_arun_lib_dynamic_dynamic=flx_arun_dynamic_dynamic_obj,
        flx_run_main_static=flx_run_main_static,
        flx_run_main_dynamic=flx_run_main_dynamic,
        flx_run_exe=flx_run_exe,
        flx_arun_main_static=flx_arun_main_static,
        flx_arun_main_dynamic=flx_arun_main_dynamic,
        flx_arun_exe=flx_arun_exe,
    )