static void NativeModuleHost_Receive(MODULE_HANDLE moduleHandle, MESSAGE_HANDLE messageHandle)
{
    if (moduleHandle != NULL)
    {
        MODULE_HOST* module_host = (MODULE_HOST*)moduleHandle;
        /*Codes_SRS_NATIVEMODULEHOST_17_030: [ NativeModuleHost_Receive shall get the loaded module's MODULE_API pointer. ]*/
        const MODULE_API* module_apis = module_host->module_loader->api->GetApi(module_host->module_loader, module_host->module_library_handle);
        if (module_apis != NULL)
        {
            pfModule_Receive pfReceive = MODULE_RECEIVE(module_apis);
            if (pfReceive != NULL)
            {
                /*Codes_SRS_NATIVEMODULEHOST_17_031: [ NativeModuleHost_Receive shall call the loaded module's _Receive function, passing the messageHandle along. ]*/
                (pfReceive)(module_host->module, messageHandle);
            }
            else
            {
                LogError("Module API did not have a Receive function");
            }
        }
        else
        {
            LogError("Module API not found");
        }
    }
    else
    {
        /*Codes_SRS_NATIVEMODULEHOST_17_029: [ NativeModuleHost_Receive shall do nothing if moduleHandle is NULL. ]*/
        LogError("Given module handle is NULL");
    }
}
/**
* This function runs for each module. It receives a pointer to a MODULE_INFO
* object that describes the module. Its job is to call the Receive function on
* the associated module whenever it receives a message.
*/
static int module_worker(void * user_data)
{
    /*Codes_SRS_BROKER_13_026: [This function shall assign `user_data` to a local variable called `module_info` of type `BROKER_MODULEINFO*`.]*/
    BROKER_MODULEINFO* module_info = (BROKER_MODULEINFO*)user_data;

    int should_continue = 1;
    while (should_continue)
    {
        /*Codes_SRS_BROKER_13_089: [ This function shall acquire the lock on module_info->socket_lock. ]*/
        if (Lock(module_info->socket_lock))
        {
            /*Codes_SRS_BROKER_02_004: [ If acquiring the lock fails, then module_worker shall return. ]*/
            LogError("unable to Lock");
            should_continue = 0;
            break;
        }
        int nn_fd = module_info->receive_socket;
        int nbytes;
        unsigned char *buf = NULL;

        /*Codes_SRS_BROKER_17_005: [ For every iteration of the loop, the function shall wait on the receive_socket for messages. ]*/
        nbytes = nn_recv(nn_fd, (void *)&buf, NN_MSG, 0);
        /*Codes_SRS_BROKER_13_091: [ The function shall unlock module_info->socket_lock. ]*/
        if (Unlock(module_info->socket_lock) != LOCK_OK)
        {
            /*Codes_SRS_BROKER_17_016: [ If releasing the lock fails, then module_worker shall return. ]*/
            should_continue = 0;
            if (nbytes > 0)
            {
                /*Codes_SRS_BROKER_17_019: [ The function shall free the buffer received on the receive_socket. ]*/
                nn_freemsg(buf);
            }
            break;
        }

        if (nbytes < 0)
        {
            /*Codes_SRS_BROKER_17_006: [ An error on receiving a message shall terminate the loop. ]*/
            should_continue = 0;
        }
        else
        {
            if (nbytes == BROKER_GUID_SIZE &&
                (strncmp(STRING_c_str(module_info->quit_message_guid), (const char *)buf, BROKER_GUID_SIZE-1)==0))
            {
                /*Codes_SRS_BROKER_13_068: [ This function shall run a loop that keeps running until module_info->quit_message_guid is sent to the thread. ]*/
                /* received special quit message for this module */
                should_continue = 0;
            }
            else
            {
                /*Codes_SRS_BROKER_17_024: [ The function shall strip off the topic from the message. ]*/
                const unsigned char*buf_bytes = (const unsigned char*)buf;
                buf_bytes += sizeof(MODULE_HANDLE);
                /*Codes_SRS_BROKER_17_017: [ The function shall deserialize the message received. ]*/
                MESSAGE_HANDLE msg = Message_CreateFromByteArray(buf_bytes, nbytes - sizeof(MODULE_HANDLE));
                /*Codes_SRS_BROKER_17_018: [ If the deserialization is not successful, the message loop shall continue. ]*/
                if (msg != NULL)
                {
                    /*Codes_SRS_BROKER_13_092: [The function shall deliver the message to the module's callback function via module_info->module_apis. ]*/
                    MODULE_RECEIVE(module_info->module->module_apis)(module_info->module->module_handle, msg);
                    /*Codes_SRS_BROKER_13_093: [ The function shall destroy the message that was dequeued by calling Message_Destroy. ]*/
                    Message_Destroy(msg);
                }
            }
            /*Codes_SRS_BROKER_17_019: [ The function shall free the buffer received on the receive_socket. ]*/
            nn_freemsg(buf);
        }    
    }

    return 0;
}
static MODULE_LIBRARY_HANDLE NodeModuleLoader_Load(const MODULE_LOADER* loader, const void* entrypoint)
{
    NODE_MODULE_HANDLE_DATA* result;

    // loader cannot be null
    if (loader == NULL)
    {
        //Codes_SRS_NODE_MODULE_LOADER_13_001 : [NodeModuleLoader_Load shall return NULL if loader is NULL.]
        result = NULL;
        LogError("invalid inputs - loader = %p", loader);
    }
    else
    {
        if (loader->type != NODEJS)
        {
            //Codes_SRS_NODE_MODULE_LOADER_13_002 : [NodeModuleLoader_Load shall return NULL if loader->type is not NODEJS.]
            result = NULL;
            LogError("loader->type is not NODEJS");
        }
        else
        {
            result = (NODE_MODULE_HANDLE_DATA*)malloc(sizeof(NODE_MODULE_HANDLE_DATA));
            if (result == NULL)
            {
                //Codes_SRS_NODE_MODULE_LOADER_13_003 : [NodeModuleLoader_Load shall return NULL if an underlying platform call fails.]
                LogError("malloc returned NULL");
            }
            else
            {
                result->binding_module = NodeModuleLoader_LoadBindingModule(loader);
                if (result->binding_module == NULL)
                {
                    LogError("NodeModuleLoader_LoadBindingModule returned NULL");
                    //Codes_SRS_NODE_MODULE_LOADER_13_003 : [NodeModuleLoader_Load shall return NULL if an underlying platform call fails.]
                    free(result);
                    result = NULL;
                }
                else
                {
                    //Codes_SRS_NODE_MODULE_LOADER_13_033: [ NodeModuleLoader_Load shall call DynamicLibrary_FindSymbol on the binding module handle with the symbol name Module_GetApi to acquire the function that returns the module's API table. ]
                    pfModule_GetApi pfnGetAPI = (pfModule_GetApi)DynamicLibrary_FindSymbol(result->binding_module, MODULE_GETAPI_NAME);
                    if (pfnGetAPI == NULL)
                    {
                        DynamicLibrary_UnloadLibrary(result->binding_module);
                        free(result);
                        //Codes_SRS_NODE_MODULE_LOADER_13_003 : [NodeModuleLoader_Load shall return NULL if an underlying platform call fails.]
                        result = NULL;
                        LogError("DynamicLibrary_FindSymbol() returned NULL");
                    }
                    else
                    {
                        //Codes_SRS_NODE_MODULE_LOADER_13_005 : [NodeModuleLoader_Load shall return a non - NULL pointer of type MODULE_LIBRARY_HANDLE when successful.]
                        result->api = pfnGetAPI(Module_ApiGatewayVersion);

                        //Codes_SRS_NODE_MODULE_LOADER_13_034: [ NodeModuleLoader_Load shall return NULL if the MODULE_API pointer returned by the binding module is NULL. ]
                        //Codes_SRS_NODE_MODULE_LOADER_13_035: [ NodeModuleLoader_Load shall return NULL if MODULE_API::version is greater than Module_ApiGatewayVersion. ]
                        //Codes_SRS_NODE_MODULE_LOADER_13_036: [ NodeModuleLoader_Load shall return NULL if the Module_Create function in MODULE_API is NULL. ]
                        //Codes_SRS_NODE_MODULE_LOADER_13_037: [ NodeModuleLoader_Load shall return NULL if the Module_Receive function in MODULE_API is NULL. ]
                        //Codes_SRS_NODE_MODULE_LOADER_13_038: [ NodeModuleLoader_Load shall return NULL if the Module_Destroy function in MODULE_API is NULL. ]

                        /* if any of the required functions is NULL then we have a misbehaving module */
                        if (result->api == NULL ||
                            result->api->version > Module_ApiGatewayVersion ||
                            MODULE_CREATE(result->api) == NULL ||
                            MODULE_DESTROY(result->api) == NULL ||
                            MODULE_RECEIVE(result->api) == NULL)
                        {
                            LogError(
                                "pfnGetapi() returned an invalid MODULE_API instance. "
                                "result->api = %p, "
                                "result->api->version = %d, "
                                "MODULE_CREATE(result->api) = %p, "
                                "MODULE_DESTROY(result->api) = %p, "
                                "MODULE_RECEIVE(result->api) = %p, ",
                                result->api,
                                result->api != NULL ? result->api->version : 0x0,
                                result->api != NULL ? MODULE_CREATE(result->api) : NULL,
                                result->api != NULL ? MODULE_DESTROY(result->api) : NULL,
                                result->api != NULL ? MODULE_RECEIVE(result->api) : NULL
                            );

                            DynamicLibrary_UnloadLibrary(result->binding_module);
                            free(result);
                            //Codes_SRS_NODE_MODULE_LOADER_13_003 : [NodeModuleLoader_Load shall return NULL if an underlying platform call fails.]
                            result = NULL;
                        }
                    }
                }
            }
        }
    }

    return result;
}