The NetWare API maintains a context for each NLM that is running. The context is divided into three levels of scope: thread level context, thread group level context, and NLM level context. (The following figure illustrates the three levels of context.) Because this context is created using the functions found in CLIB.NLM, it is commonly known as CLIB context.
Figure 1-5 Levels of NLM Context

NOTE:An understanding of context is critical to NLM development. Many errors in NLM applications are caused by developers not understanding context and how it can change.
Threads created in one of the four ways described in Section 1.4, Context and Thread Groups have the CLIB thread level, group level, and NLM level context. These context levels contain different information that is changed by the NetWare API. The context information cannot be changed directly by the programmer.
NOTE:A fifth way to create threads is for the OS to create threads. These threads do not have CLIB context, and must be given CLIB context. This issue is discussed after the following discussion about the context levels.
Thread level context is the most private level of context information within an NLM; the context of each thread is available only to each thread. These values are separate for each thread. The data items of one thread cannot be referenced by another thread.
Threads maintain the following context:
All of the threads in a thread group share the same thread group level context. Any change that one thread makes to the value of a thread group data item affects all the threads in the group. The context of a thread group, however, is not shared with other thread groups, so changes within one thread group do not affect another group.
Thread groups maintain the following context:
The NLM™ level context is shared by all thread groups and threads in the NLM, and these data items have only one value for the entire NLM. The data items are global to all the thread groups and threads in the NLM. Any changes made to the values of NLM global data items affect all the thread groups and threads in the NLM.
NLM applications maintain the following context on a per NLM basis:
There are two types of threads running in the NetWare® OS: OS threads (such as callbacks) and CLIB threads (those created by the CLIB.NLM functions). The OS threads are created by the OS in instances such as when the LOAD and the UNLOAD commands are used. CLIB threads are created by calling the NetWare API functions BeginThread, ScheduleWorkToDo, and BeginThreadGroup, and by a default thread starting at the main function. The following figure shows that OS threads are missing the context that CLIB threads have.
Figure 1-6 Context of OS and CLIB Threads

The problem here is that many-but not all-NetWare API functions need to have a context in order to work correctly. For example: printf writes to the calling thread’s current screen. The current screen is kept in the thread’s thread group context. OS threads do not have any CLIB context, so their calls to printf do not produce output anywhere. In more extreme cases, OS threads calling the NetWare API functions that need CLIB context can cause the server to abend.
NOTE:The solution to this problem is to give the OS threads context, thereby turning them into CLIB threads. The method for doing this is presented in the following section.
Developers must be aware of all the situations where NLMs will be running with OS threads instead of CLIB threads, and adjust their code accordingly to give the OS threads CLIB context. The following is a list of conditions where the NLM runs with OS threads:
In the following conditions, threads might or might not have CLIB context, depending on the context specifier:
Two solutions to these context problems are as follows:
NetWare 3.11, 4.x, 5.x, and 6.x solution: read group ID of one of the groups, such as for the default thread group created for the main function. (You might also want to create a global variable for each of the thread groups that are created.) The thread group ID of the current thread group can be obtained with GetThreadGroupID, as shown in the following example:
#include <process.h>
int globalThreadGroupID;
main()
{
globalThreadGroupID = GetThreadGroupID();
...
}
Then, when you have an OS thread running, you give the OS thread context using SetThreadGroupID as follows:
oldTGID = SetThreadGroupID(globalThreadGroupID); /* do work */ ... SetThreadGroupID(oldTGID); /* always set back the thread group ID */
At this point, the NetWare® API takes the OS thread and gives it context, just as if it had been a CLIB thread. This lets you use the NetWare API functions that need context.
IMPORTANT:You must be careful when using the thread group ID that other threads are using. Changes to the context affect all threads in that group.
NetWare 4.x, 5.x, and 6.x solution: CLIB threads in the NetWare 4.x, 5.x, and 6.x OS have been given a context specifier that gives these threads the ability to automatically give context to callbacks (OS threads that are registered to be called to run when specific conditions occur) that they register. The context that is given to the callbacks when they are registered is determined by the setting of the registering thread’s context specifier. The context specifier can be set to one of the following settings:
Call SetThreadGroupID and pass in a valid thread group ID. Use this once inside your callback to give your callback thread CLIB context.
You can determine the existing setting of the registering thread’s context specifier by calling GetThreadContextSpecifier. Call SetThreadContextSpecifier to set a thread’s context specifier.
When a new thread is started with BeginThread, BeginThreadGroup, or ScheduleWorkToDo, its context specifier is set to USE_CURRENT_CONTEXT by default.
Using this solution, if you want the registered thread to have the thread group context of the registering thread, you would set the registering thread’s context specifier to USE_CURRENT_CONTEXT (if it has been changed from the default) and then register the function that will run as a callback.
NOTE:The drawback to using this solution is that the context specifier is specific to the NetWare 4.x, 5.x, and 6.x OS. If you use this solution, your NLM will not run on the NetWare 3.11 OS.