Table of Contents


Single-threaded design philosophy

NxCore API was designed to be single-threaded, and internally uses thread-specific (thread local storage, or TLS) data structures to allow complete isolation between multiple threads running the API.

Each of your multiple NxCore processing threads is completely isolated from others.

Each API call you make works on the data within your thread.

All API calls are deterministic and synchronous.

Parallel processing

The single-threaded design allows you to have a large number of independent threads processing different tapes at the same time, with zero synchronization requirements.

Just start a thread, have that thread call ProcessTape(), and that thread will process that tape without you worrying about any further threading/synchronization issues.

Use of global variables

The only global variable that's safe to use in multiple NxCore threads is NxCoreClass.

While you can use __declspec(thread) for your global variables, a better way is to create a struct that will contain all your global data, and pass a pointer to that structure in the UserData field of ProcessTape().

Look at the example below -- the PerThreadData structure is used to simulate global variables.

Lifetime of NxCore data structures

All NxCore data structures are allocated at beginning of ProcessTape, and destroyed before ProcessTape returns.

NxString values remain constant, but virtually all other structures get overwritten on every callback.

If you're looking to use your own synchronization mechanisms, you'll need to manage copying the data off yourself.

Example

Here's an example of a very simple, thread-safe, multiple-tape message counter

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <process.h>    
  
#include "NxCoreAPI.h"
#include "NxCoreAPI_class.h"    
  
static NxCoreClass nxCoreClass;    
  
struct PerThreadData
{
    __int64 counter;
    int rc;
    int time;
    const char* tape;
    PerThreadData() : counter(0), rc(0), time(0), tape(0) {}
};    
  
static int __stdcall nxCoreCallback(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMessage)
{
    PerThreadData *perThreadData = (PerThreadData*) pNxCoreSys->UserData;    
  
    perThreadData->counter++;    
  
    if ((pNxCoreMessage->MessageType==NxMSG_STATUS)&&(pNxCoreSys->ClockUpdateInterval >= NxCLOCK_MINUTE))
    {  
      printf("Tape Date: %02d/%02d/%d  Tape Time: %02d:%02d:%02d\n",
             pNxCoreSys->nxDate.Month,pNxCoreSys->nxDate.Day,pNxCoreSys->nxDate.Year,
             pNxCoreSys->nxTime.Hour,pNxCoreSys->nxTime.Minute,pNxCoreSys->nxTime.Second);
    }    
  
    return NxCALLBACKRETURN_CONTINUE;
}    
  
static void handleTapeThread(void *arg)
{
    PerThreadData *perThreadData = (PerThreadData*) arg;
    long startTick = GetTickCount();
    perThreadData->rc = nxCoreClass.ProcessTape(perThreadData->tape, 0, 0, (int) perThreadData, nxCoreCallback);
    perThreadData->time = GetTickCount() - startTick;
}    
  
int main(int argc, char** argv)
{
    if (argc == 1)
    {
        fprintf(stderr, "%s tape1 [tape2 tape3 ...]\n", argv[0]);
        return -1;
    }    
  
    if (!nxCoreClass.LoadNxCore("NxCoreAPI.dll") &&
        !nxCoreClass.LoadNxCore("C:\\Program Files\\Nanex\\NxCoreAPI\\NxCoreAPI.dll"))
    {
        fprintf(stderr, "Can't find NxCoreAPI.dll\n");
        return -1;
    }    
  
    const int numTapes = argc - 1;
    HANDLE *threads = new HANDLE[numTapes];
    PerThreadData *perThreadData = new PerThreadData[numTapes];    
  
    for (int i = 0; i < numTapes; i++)
    {
        perThreadData[i].tape = argv[i + 1];
        printf("Start Tape: %s\n",perThreadData[i].tape);
        threads[i] = (HANDLE) _beginthread(handleTapeThread, 0, (void*) &perThreadData[i]);
    }    
  
    WaitForMultipleObjects(numTapes, threads, TRUE, INFINITE);    
  
    for (int i = 0; i < numTapes; i++)
    {
        const PerThreadData& ptd = perThreadData[i];
        printf("Tape[%s] Count[%I64d] RC[%ld] Millis[%ld]\n", ptd.tape, ptd.counter, ptd.rc, ptd.time);
    }    
  
    delete threads;
    delete perThreadData;
    return 0;
}