Exemple #1
0
gint master_run(Master* master) {
    MAGIC_ASSERT(master);

    message("loading and initializing simulation data");

    /* start loading and initializing simulation data */
    _master_loadConfiguration(master);
    gboolean isSuccess = _master_loadTopology(master);
    if(!isSuccess) {
        return 1;
    }

    _master_initializeTimeWindows(master);

    /* the master will be responsible for distributing the actions to the slaves so that
     * they all have a consistent view of the simulation, topology, etc.
     * For now we only have one slave so send it everything. */
    guint slaveSeed = random_nextUInt(master->random);
    master->slave = slave_new(master, master->options, master->endTime, slaveSeed);

    message("registering plugins and hosts");

    /* register the components needed by each slave.
     * this must be done after slaves are available so we can send them messages */
    _master_registerPlugins(master);
    _master_registerHosts(master);

    message("running simulation");

    /* dont buffer log messages in debug mode */
    if(options_getLogLevel(master->options) != LOGLEVEL_DEBUG) {
        message("log message buffering is enabled for efficiency");
        logger_setEnableBuffering(logger_getDefault(), TRUE);
    }

    /* start running each slave */
    slave_run(master->slave);

    /* only need to disable buffering if it was enabled, otherwise
     * don't log the message as it may confuse the user. */
    if(options_getLogLevel(master->options) != LOGLEVEL_DEBUG) {
        message("log message buffering is disabled during cleanup");
        logger_setEnableBuffering(logger_getDefault(), FALSE);
    }

    message("simulation finished, cleaning up now");

    return slave_free(master->slave);
}
Exemple #2
0
Event* scheduler_pop(Scheduler* scheduler) {
    MAGIC_ASSERT(scheduler);

    /* this function should block until a non-null event is available for the worker to run.
     * return NULL only to signal the worker thread to quit */

    while(scheduler->isRunning) {
        /* pop from a queue based on the policy */
        Event* nextEvent = scheduler->policy->pop(scheduler->policy, scheduler->currentRound.endTime);

        if(nextEvent != NULL) {
            /* we have an event, let the worker run it */
            return nextEvent;
        } else if(scheduler->policyType == SP_SERIAL_GLOBAL) {
            /* the running thread has no more events to execute this round, but we only have a
             * single, global, serial queue, so returning NULL without blocking is OK. */
            return NULL;
        } else {
            /* the running thread has no more events to execute this round and we need to block it
             * so that we can wait for all threads to finish events from this round. We want to
             * track idle times, so let's start by making sure we have timer elements in place. */
            GTimer* executeEventsBarrierWaitTime = g_hash_table_lookup(scheduler->threadToWaitTimerMap, GUINT_TO_POINTER(pthread_self()));

            /* wait for all other worker threads to finish their events too, and track wait time */
            if(executeEventsBarrierWaitTime) {
                g_timer_continue(executeEventsBarrierWaitTime);
            }
            countdownlatch_countDownAwait(scheduler->executeEventsBarrier);
            if(executeEventsBarrierWaitTime) {
                g_timer_stop(executeEventsBarrierWaitTime);
            }

            /* now all threads reached the current round end barrier time.
             * asynchronously collect some stats that the main thread will use. */
            if(scheduler->policy->getNextTime) {
                SimulationTime nextTime = scheduler->policy->getNextTime(scheduler->policy);
                g_mutex_lock(&(scheduler->globalLock));
                scheduler->currentRound.minNextEventTime = MIN(scheduler->currentRound.minNextEventTime, nextTime);
                g_mutex_unlock(&(scheduler->globalLock));
            }

            /* clear all log messages from the last round */
            logger_flushRecords(logger_getDefault(), pthread_self());

            /* wait for other threads to finish their collect step */
            countdownlatch_countDownAwait(scheduler->collectInfoBarrier);

            /* now wait for main thread to process a barrier update for the next round */
            countdownlatch_countDownAwait(scheduler->prepareRoundBarrier);
        }
    }

    /* scheduler is done, return NULL to stop worker */
    return NULL;
}
Exemple #3
0
gint main_runShadow(gint argc, gchar* argv[]) {
    /* check the compiled GLib version */
    if (!GLIB_CHECK_VERSION(2, 32, 0)) {
        g_printerr("** GLib version 2.32.0 or above is required but Shadow was compiled against version %u.%u.%u\n",
            (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION);
        return EXIT_FAILURE;
    }

    if(GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION == 40) {
        g_printerr("** You compiled against GLib version %u.%u.%u, which has bugs known to break Shadow. Please update to a newer version of GLib.\n",
                    (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION);
        return EXIT_FAILURE;
    }

    /* check the that run-time GLib matches the compiled version */
    const gchar* mismatch = glib_check_version(GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
    if(mismatch) {
        g_printerr("** The version of the run-time GLib library (%u.%u.%u) is not compatible with the version against which Shadow was compiled (%u.%u.%u). GLib message: '%s'\n",
        glib_major_version, glib_minor_version, glib_micro_version,
        (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION,
        mismatch);
        return EXIT_FAILURE;
    }

    /* parse the options from the command line */
    gchar* cmds = g_strjoinv(" ", argv);
    gchar** cmdv = g_strsplit(cmds, " ", 0);
    g_free(cmds);
    Options* options = options_new(argc, cmdv);
    g_strfreev(cmdv);
    if(!options) {
        return EXIT_FAILURE;
    }

    /* if they just want the shadow version, print it and exit */
    if(options_doRunPrintVersion(options)) {
        g_printerr("%s running GLib v%u.%u.%u and IGraph v%s\n%s\n",
                SHADOW_VERSION_STRING,
                (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION,
#if defined(IGRAPH_VERSION)
                IGRAPH_VERSION,
#else
                "(n/a)",
#endif
                SHADOW_INFO_STRING);
        options_free(options);
        return EXIT_SUCCESS;
    }

    /* start up the logging subsystem to handle all future messages */
    Logger* shadowLogger = logger_new(options_getLogLevel(options));
    logger_setDefault(shadowLogger);

    /* disable buffering during startup so that we see every message immediately in the terminal */
    logger_setEnableBuffering(shadowLogger, FALSE);

    gint returnCode = _main_helper(options);

    options_free(options);
    Logger* logger = logger_getDefault();
    if(logger) {
        logger_setDefault(NULL);
        logger_unref(logger);
    }

    g_printerr("** Stopping Shadow, returning code %i (%s)\n", returnCode, (returnCode == 0) ? "success" : "error");
    return returnCode;
}
Exemple #4
0
static gint _main_helper(Options* options) {
    /* start off with some status messages */
#if defined(IGRAPH_VERSION)
    gint igraphMajor = -1, igraphMinor = -1, igraphPatch = -1;
    igraph_version(NULL, &igraphMajor, &igraphMinor, &igraphPatch);

    gchar* startupStr = g_strdup_printf("Starting %s with GLib v%u.%u.%u and IGraph v%i.%i.%i",
            SHADOW_VERSION_STRING,
            (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION,
            igraphMajor, igraphMinor, igraphPatch);
#else
    gchar* startupStr = g_strdup_printf("Starting %s with GLib v%u.%u.%u (IGraph version not available)",
            SHADOW_VERSION_STRING,
            (guint)GLIB_MAJOR_VERSION, (guint)GLIB_MINOR_VERSION, (guint)GLIB_MICRO_VERSION);
#endif

    message("%s", startupStr);
    /* avoid logging the message to stderr twice (only log if this is not a relaunch) */
    if(g_getenv("SHADOW_SPAWNED") == NULL) {
        g_printerr("** %s\n", startupStr);
    }
    g_free(startupStr);

    message(SHADOW_INFO_STRING);
    message("logging current startup arguments and environment");

    gchar** envlist = g_get_environ();
    gchar** arglist = g_strsplit(options_getArgumentString(options), " ", 0);
    _main_logEnvironment(arglist, envlist);
    g_strfreev(arglist);
    g_strfreev(envlist);

    /* check if we still need to setup our required environment and relaunch */
    if(g_getenv("SHADOW_SPAWNED") == NULL) {
        message("shadow will automatically adjust environment and relaunch");
        message("loading shadow configuration file");

        /* we need to relaunch.
         * first lets load the config file to help us setup the environment */
        const GString* fileName = options_getInputXMLFilename(options);
        if(!fileName) {
            return EXIT_FAILURE;
        }

        GString* file = utility_getFileContents(fileName->str);
        if(!file) {
            critical("unable to read config file contents");
            return EXIT_FAILURE;
        }

        Configuration* config = configuration_new(options, file);
        g_string_free(file, TRUE);
        file = NULL;
        if(!config) {
            critical("there was a problem parsing the Shadow config file, and we can't run without it");
            return EXIT_FAILURE;
        }

        message("shadow configuration file loaded, parsed, and passed validation");

        /* now start to set up the environment */
        gchar** envlist = g_get_environ();
        GString* commandBuffer = g_string_new(options_getArgumentString(options));

        if(!envlist || !commandBuffer) {
            critical("there was a problem loading existing environment");
            configuration_free(config);
            return EXIT_FAILURE;
        }

        message("setting up LD_PRELOAD environment");

        /* compute the proper LD_PRELOAD value, extract the shadow preload file and
         * set it as a command line argument if needed */
        gchar* preloadArgValue = NULL;
        {
            /* before we restart, we should:
             *  -set the shadow preload lib as a command line arg
             *  -make sure it does not exist in LD_PRELOAD, but otherwise leave LD_PRELOAD in place
             * we need to search for our preload lib. our order of preference follows.
             */

            /* 1. existing "--preload=" option value */

            if(_main_isValidPathToPreloadLib(options_getPreloadString(options))) {
                preloadArgValue = g_strdup(options_getPreloadString(options));
            }

            /* 2. the 'preload' attribute value of the 'shadow' element in shadow.config.xml */

            /* we only need to search if we haven't already found a valid path */
            if(!preloadArgValue) {
                ConfigurationShadowElement* element = configuration_getShadowElement(config);
                if(element && element->preloadPath.isSet) {
                    gchar* path = element->preloadPath.string->str;
                    if(_main_isValidPathToPreloadLib(path)) {
                        preloadArgValue = g_strdup(path);
                    }
                }
            }

            /* 3. the LD_PRELOAD value */

            GString* preloadEnvValueBuffer = NULL;
            /* we always search the env variable and remove existing Shadow preload libs */
            if(g_environ_getenv(envlist, "LD_PRELOAD") != NULL) {
                gchar** tokens = g_strsplit(g_environ_getenv(envlist, "LD_PRELOAD"), ":", 0);

                for(gint i = 0; tokens[i] != NULL; i++) {
                    /* each token in the env variable should be an absolute path */
                    if(_main_isValidPathToPreloadLib(tokens[i])) {
                        /* found a valid path, only save it if we don't have one yet (from options or config) */
                        if(!preloadArgValue) {
                            preloadArgValue = g_strdup(tokens[i]);
                        }
                    } else {
                        /* maintain non-shadow entries */
                        if(preloadEnvValueBuffer) {
                            g_string_append_printf(preloadEnvValueBuffer, ":%s", tokens[i]);
                        } else {
                            preloadEnvValueBuffer = g_string_new(tokens[i]);
                        }
                    }
                }

                g_strfreev(tokens);
            }

            /* 4. the 'environment' attribute of the 'shadow' configuration element in shadow.config.xml */

            /* always go through the 'environment' attribute of the 'shadow' element to pull in any keys defined there */
            {
                ConfigurationShadowElement* element = configuration_getShadowElement(config);
                if(element && element->environment.isSet) {
                    /* entries are split by ';' */
                    gchar** envTokens = g_strsplit(element->environment.string->str, ";", 0);

                    for(gint i = 0; envTokens[i] != NULL; i++) {
                        /* each env entry is key=value, get 2 tokens max */
                        gchar** items = g_strsplit(envTokens[i], "=", 2);

                        gchar* key = items[0];
                        gchar* value = items[1];

                        if(key != NULL && value != NULL) {
                            /* check if the key is LD_PRELOAD */
                            if(!g_ascii_strncasecmp(key, "LD_PRELOAD", 10)) {
                                gchar** preloadTokens = g_strsplit(value, ":", 0);

                                for(gint j = 0; preloadTokens[j] != NULL; j++) {
                                    /* each token in the env variable should be an absolute path */
                                    if(_main_isValidPathToPreloadLib(preloadTokens[j])) {
                                        /* found a valid path, only save it if we don't have one yet (from options or config) */
                                        if(!preloadArgValue) {
                                            preloadArgValue = g_strdup(preloadTokens[j]);
                                        }
                                    } else {
                                        /* maintain non-shadow entries */
                                        if(preloadEnvValueBuffer) {
                                            g_string_append_printf(preloadEnvValueBuffer, ":%s", preloadTokens[j]);
                                        } else {
                                            preloadEnvValueBuffer = g_string_new(preloadTokens[j]);
                                        }
                                    }
                                }

                                g_strfreev(preloadTokens);
                            } else {
                                /* set the key=value pair, but don't overwrite any existing settings */
                                envlist = g_environ_setenv(envlist, key, value, 0);
                            }
                        }

                        g_strfreev(items);
                    }

                    g_strfreev(envTokens);
                }
            }

            /* save away the non-shadow preload libs */
            if(preloadEnvValueBuffer) {
                envlist = g_environ_setenv(envlist, "LD_PRELOAD", preloadEnvValueBuffer->str, 1);
                g_string_free(preloadEnvValueBuffer, TRUE);
                preloadEnvValueBuffer = NULL;
            } else {
                envlist = g_environ_unsetenv(envlist, "LD_PRELOAD");
            }

            /* 5. as a last hope, try looking in RPATH since shadow is built with one */

            /* we only need to search if we haven't already found a valid path */
            if(!preloadArgValue) {
                gchar* rpathStr = _main_getRPath();
                if(rpathStr != NULL) {
                    gchar** tokens = g_strsplit(rpathStr, ":", 0);

                    for(gint i = 0; tokens[i] != NULL; i++) {
                        GString* candidateBuffer = g_string_new(NULL);

                        /* rpath specifies directories, so look inside */
                        g_string_printf(candidateBuffer, "%s/%s", tokens[i], INTERPOSELIBSTR);
                        gchar* candidate = g_string_free(candidateBuffer, FALSE);

                        if(_main_isValidPathToPreloadLib(candidate)) {
                            preloadArgValue = candidate;
                            break;
                        } else {
                            g_free(candidate);
                        }
                    }

                    g_strfreev(tokens);
                }
                g_free(rpathStr);
            }

            /* if we still didn't find our preload lib, that is a user error */
            if(!preloadArgValue) {
                critical("can't find path to %s, did you specify an absolute path to an existing readable file?", INTERPOSELIBSTR);
                configuration_free(config);
                return EXIT_FAILURE;
            }

            /* now that we found the correct path to the preload lib, first remove any possibly
             * incomplete path that might exist in the command line args, and then replace it
             * with the path that we found and verified is correct. */

            {
                /* first remove all preload options */
                gchar** tokens = g_strsplit(commandBuffer->str, " ", 0);
                g_string_free(commandBuffer, TRUE);
                commandBuffer = NULL;

                for(gint i = 0; tokens[i] != NULL; i++) {
                    /* use -1 to search the entire string */
                    if(!g_ascii_strncasecmp(tokens[i], "--preload=", 10)) {
                        /* skip this key=value string */
                    } else if(!g_ascii_strncasecmp(tokens[i], "-p", 2)) {
                        /* skip this key, and also the next arg which is the value */
                        i++;
                    } else {
                        if(commandBuffer) {
                            g_string_append_printf(commandBuffer, " %s", tokens[i]);
                        } else {
                            commandBuffer = g_string_new(tokens[i]);
                        }
                    }
                }

                g_strfreev(tokens);

                /* now add back in the preload option */
                g_string_append_printf(commandBuffer, " --preload=%s", preloadArgValue);
            }

        }

        message("setting up LD_STATIC_TLS_EXTRA environment");

        /* compute the proper TLS size we need for dlmopen()ing all of the plugins,
         * but only do this if the user didn't manually specify a size */
        if(g_environ_getenv(envlist, "LD_STATIC_TLS_EXTRA") == NULL) {
            gchar* staticTLSValue = _main_getStaticTLSValue(options, config, preloadArgValue);
            envlist = g_environ_setenv(envlist, "LD_STATIC_TLS_EXTRA", staticTLSValue, 0);
            message("we need %s total bytes of static TLS storage", staticTLSValue);
            g_free(staticTLSValue);
        }

        /* cleanup unused string */
        if(preloadArgValue) {
            g_free(preloadArgValue);
            preloadArgValue = NULL;
        }

        /* are we running valgrind */
        if(options_doRunValgrind(options)) {
            message("setting up environment for valgrind");

            /* make glib friendlier to valgrind */
            envlist = g_environ_setenv(envlist, "G_DEBUG", "gc-friendly", 0);
            envlist = g_environ_setenv(envlist, "G_SLICE", "always-malloc", 0);

            /* add the valgrind command and some default options */
            g_string_prepend(commandBuffer,
                            "valgrind --leak-check=full --show-reachable=yes --track-origins=yes --trace-children=yes --log-file=shadow-valgrind-%p.log --error-limit=no ");
        } else {
            /* The following can be used to add internal GLib memory validation that
             * will abort the program if it finds an error. This is only useful outside
             * of the valgrind context, as otherwise valgrind will complain about
             * the implementation of the GLib validator.
             * e.g. $ G_SLICE=debug-blocks shadow --file
             *
             * envlist = g_environ_setenv(envlist, "G_SLICE", "debug-blocks", 0);
             */
        }

        /* keep track that we are relaunching shadow */
        envlist = g_environ_setenv(envlist, "SHADOW_SPAWNED", "TRUE", 1);

        gchar* command = g_string_free(commandBuffer, FALSE);
        gchar** arglist = g_strsplit(command, " ", 0);
        g_free(command);

        configuration_free(config);

        message("environment was updated; shadow is relaunching now with new environment");

        Logger* logger = logger_getDefault();
        if(logger) {
            logger_setDefault(NULL);
            logger_unref(logger);
        }

        /* execvpe only returns if there is an error, otherwise the current process
         * image is replaced with a new process */
        gint returnValue = execvpe(arglist[0], arglist, envlist);

        /* cleanup */
        if(envlist) {
            g_strfreev(envlist);
        }
        if(arglist) {
            g_strfreev(arglist);
        }

        critical("** Error %i while re-launching shadow process: %s", returnValue, g_strerror(returnValue));
        return EXIT_FAILURE;
    }

    /* we dont need to relaunch, so we can run the simulation */

    /* make sure we have initialized static tls */
    if(!_main_verifyStaticTLS()) {
        critical("** Shadow Setup Check Failed: LD_STATIC_TLS_EXTRA does not contain a nonzero value");
        return EXIT_FAILURE;
    }

    /* make sure we have the shadow preload lib */
    if(!_main_isValidPathToPreloadLib(options_getPreloadString(options))) {
        critical("** Shadow Setup Check Failed: cannot find absolute path to "INTERPOSELIBSTR"");
        return EXIT_FAILURE;
    }

    /* now load the preload library into shadow's namespace */
    if(!_main_loadShadowPreload(options)) {
        critical("** Shadow Setup Check Failed: unable to load preload library");
        return EXIT_FAILURE;
    }

    /* tell the preload lib we are ready for action */
    extern int interposer_setShadowIsLoaded(int);
    int interposerResult = interposer_setShadowIsLoaded(1);

    if(interposerResult != 0) {
        /* it was not intercepted, meaning our preload library is not set up properly */
        critical("** Shadow Setup Check Failed: preload library is not correctly interposing functions");
        return EXIT_FAILURE;
    }

    message("startup checks passed, we are ready to start simulation");

    /* pause for debugger attachment if the option is set */
    if(options_doRunDebug(options)) {
        gint pid = (gint)getpid();
        message("Pausing with SIGTSTP to enable debugger attachment (pid %i)", pid);
        g_printerr("** Pausing with SIGTSTP to enable debugger attachment (pid %i)\n", pid);
        raise(SIGTSTP);
        message("Resuming now");
    }

    /* allocate and initialize our main simulation driver */
    gint returnCode = 0;
    shadowMaster = master_new(options);

    message("log message buffering is enabled for efficiency");
    logger_setEnableBuffering(logger_getDefault(), TRUE);

    if(shadowMaster) {
        /* run the simulation */
        returnCode = master_run(shadowMaster);
        /* cleanup */
        master_free(shadowMaster);
        shadowMaster = NULL;
    }

    message("%s simulation was shut down cleanly, returning code %i", SHADOW_VERSION_STRING, returnCode);
    return returnCode;
}
Exemple #5
0
Scheduler* scheduler_new(SchedulerPolicyType policyType, guint nWorkers, gpointer threadUserData,
        guint schedulerSeed, SimulationTime endTime) {
    Scheduler* scheduler = g_new0(Scheduler, 1);
    MAGIC_INIT(scheduler);

    /* global lock */
    g_mutex_init(&(scheduler->globalLock));

    scheduler->startBarrier = countdownlatch_new(nWorkers+1);
    scheduler->finishBarrier = countdownlatch_new(nWorkers+1);
    scheduler->executeEventsBarrier = countdownlatch_new(nWorkers+1);
    scheduler->collectInfoBarrier = countdownlatch_new(nWorkers+1);
    scheduler->prepareRoundBarrier = countdownlatch_new(nWorkers+1);

    scheduler->endTime = endTime;
    scheduler->currentRound.endTime = scheduler->endTime;// default to one single round
    scheduler->currentRound.minNextEventTime = SIMTIME_MAX;

    scheduler->threadToWaitTimerMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_timer_destroy);
    scheduler->hostIDToHostMap = g_hash_table_new(g_direct_hash, g_direct_equal);

    scheduler->random = random_new(schedulerSeed);

    /* ensure we have sane default modes for the number of workers we are using */
    if(nWorkers == 0) {
        scheduler->policyType = SP_SERIAL_GLOBAL;
    } else if(nWorkers > 0 && policyType == SP_SERIAL_GLOBAL) {
        policyType = SP_PARALLEL_HOST_STEAL;
    } else {
        scheduler->policyType = policyType;
    }

    /* create the configured policy to handle queues */
    switch(scheduler->policyType) {
        case SP_PARALLEL_HOST_SINGLE: {
            scheduler->policy = schedulerpolicyhostsingle_new();
            break;
        }
        case SP_PARALLEL_HOST_STEAL: {
            scheduler->policy = schedulerpolicyhostsingle_new();
            break;
        }
        case SP_PARALLEL_THREAD_SINGLE: {
            scheduler->policy = schedulerpolicythreadsingle_new();
            break;
        }
        case SP_PARALLEL_THREAD_PERTHREAD: {
            scheduler->policy = schedulerpolicythreadperthread_new();
            break;
        }
        case SP_PARALLEL_THREAD_PERHOST: {
            scheduler->policy = schedulerpolicythreadperhost_new();
            break;
        }
        case SP_SERIAL_GLOBAL:
        default: {
            scheduler->policy = schedulerpolicyglobalsingle_new();
            break;
        }
    }
    utility_assert(scheduler->policy);

    /* make sure our ref count is set before starting the threads */
    scheduler->referenceCount = 1;

    scheduler->threadItems = g_queue_new();

    /* start up threads and create worker storage, each thread will call worker_new,
     * and wait at startBarrier until we are ready to launch */
    for(gint i = 0; i < nWorkers; i++) {
        GString* name = g_string_new(NULL);
        g_string_printf(name, "worker-%i", (i));

        SchedulerThreadItem* item = g_new0(SchedulerThreadItem, 1);
        item->notifyDoneRunning = countdownlatch_new(1);

        WorkerRunData* runData = g_new0(WorkerRunData, 1);
        runData->userData = threadUserData;
        runData->scheduler = scheduler;
        runData->threadID = i;
        runData->notifyDoneRunning = item->notifyDoneRunning;

        gint returnVal = pthread_create(&(item->thread), NULL, (void*(*)(void*))worker_run, runData);
        if(returnVal != 0) {
            critical("unable to create worker thread");
            return NULL;
        }

        returnVal = pthread_setname_np(item->thread, name->str);
        if(returnVal != 0) {
            warning("unable to set name of worker thread to '%s'", name->str);
        }
        utility_assert(item->thread);

        g_queue_push_tail(scheduler->threadItems, item);
        logger_register(logger_getDefault(), item->thread);

        g_string_free(name, TRUE);
    }
    message("main scheduler thread will operate with %u worker threads", nWorkers);

    return scheduler;
}