1 /* This is the file to support OS/2 threads.
2  * This file is under construction. We partially support EMX. The EMX
3  * environment must currently be "OS2". Thus, usage is provided for
4  * makefile.os2.emx. Others may run.
5  * We initialize the global data structure and the global access variable.
6  */
7 
8 #define INCL_NOCOMMON
9 #define INCL_DOSPROCESS
10 #define INCL_DOSSEMAPHORES
11 #define INCL_DOSERRORS
12 #define INCL_DOSMEMMGR
13 
14 #ifdef __EMX__
15 # define DONT_TYPEDEF_PFN
16 # include <io.h>
17 # include <os2emx.h>
18 #elif defined (__WATCOMC__)
19 # define DONT_TYPEDEF_PFN
20 # include <os2.h>
21 #endif
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29 
30 #include "rxmt.h"
31 
32 static ULONG *TSD_ptrs = NULL;
33 
34 typedef struct _MT_mem
35 {
36    struct _MT_mem *prev;
37    struct _MT_mem *next;
38    /* real data follows here */
39 } MT_mem;
40 
41 /* mt_tsd: static variables of this module (thread-safe) */
42 typedef struct
43 {
44    MT_mem *mem_base; /* From DosAllocMem, we use this memory scheme since it is
45                       * native OS/2. emx has its own heap allocation functions.
46                       */
47 } mt_tsd_t; /* thread-specific but only needed by this module. see
48              * RxPackInitializeThread
49              */
50 
51 extern void * INIT_RXPACKAGE (RxPackageGlobalDataDef *);
52 extern void TERM_RXPACKAGE (RxPackageGlobalDataDef *);
53 
54 /* Returns a pointer to the TSD pointer */
FindThreadDataIdx(void)55 static RxPackageGlobalDataDef **FindThreadDataIdx(void)
56 {
57    RxPackageGlobalDataDef **retval = NULL;
58    ULONG rc;
59 
60    DosEnterCritSec();
61    if (TSD_ptrs != NULL)
62    {
63       retval = (RxPackageGlobalDataDef **) TSD_ptrs;
64       goto Leave;
65    }
66    if ((rc = DosAllocThreadLocalMemory(1,&TSD_ptrs)) != NO_ERROR)
67    {
68       fprintf(stderr,"rc = %lu from DosAllocThreadLocalMemory, await SIGSEGV!\n",
69                      (unsigned long) rc);
70       TSD_ptrs = NULL;
71    }
72    if (TSD_ptrs != NULL)
73       retval = (RxPackageGlobalDataDef **) TSD_ptrs;
74 Leave:
75    DosExitCritSec();
76    return(retval);
77 }
78 
RxPackDeinitialize(void * buf)79 static void RxPackDeinitialize(void *buf)
80 {
81    RxPackageGlobalDataDef *TSD = buf;
82    mt_tsd_t *mt;
83    MT_mem *chunk;
84 
85    if (TSD == NULL) /* Should never happen but be sure */
86       return;
87 
88    TERM_RXPACKAGE(TSD);
89 
90    mt = TSD->mt_tsd;
91    if (mt)
92    {
93       while ((chunk = mt->mem_base) != NULL)
94       {
95          TSD->RxMTFree(TSD,chunk + 1);
96          if (mt->mem_base == chunk)
97             break; /* something goes wrong. Don't run into an endless loop */
98       }
99       free(mt);
100    }
101    free(TSD);
102 }
103 
IfcRxPackCleanup(VOID)104 int IfcRxPackCleanup( VOID )
105 {
106    RxPackageGlobalDataDef **ptsd = FindThreadDataIdx();
107 
108    if (*ptsd == NULL)
109       return 0;
110    RxPackDeinitialize(*ptsd);
111    *ptsd = NULL;
112 
113    return 1;
114 }
115 
116 #ifdef DYNAMIC
117 /* Deinitialize is called when the thread terminates.
118  * This is a wonderful position to place code which frees all allocated stuff!
119  */
120 #ifdef __EMX__
121 /* We provide a DLL entry function. Look at the standard documentation for
122  * EMX. See emxdev.doc and testdll6.c. We may use the macros CRT_INIT1 and
123  * CRT_TERM1 from sys/startup.h instead but that's undocumented stuff.
124  */
125 int _CRT_init (void);
126 void _CRT_term (void);
127 void __ctordtorInit (void);
128 void __ctordtorTerm (void);
129 
_DLL_InitTerm(unsigned long mod_handle,unsigned long flag)130 unsigned long _DLL_InitTerm (unsigned long mod_handle, unsigned long flag)
131 {
132    switch (flag)
133    {
134       case 0:
135          if (_CRT_init() != 0)
136             return 0;
137          __ctordtorInit();
138          return 1;
139       case 1:
140          /*
141           * This will run ONLY if called on dynamic unload of the complete
142           * library. This means, the last thread will receive an unload
143           * command. Stupid OS/2.
144           */
145          IfcRxPackCleanup();
146          __ctordtorTerm();
147          _CRT_term();
148          return 1;
149       default:
150          break;
151    }
152    return 0;
153 }
154 #endif /* EMX */
155 #endif /* DYNAMIC */
156 
157 /* This should prevent some error messages and is used as a #define */
sizeof_ptr(void)158 static unsigned sizeof_ptr(void)
159 {
160    return(sizeof(void *));
161 }
162 
163 /* Lowest level memory allocation function for normal circumstances. */
RxMTMalloc(const RxPackageGlobalDataDef * TSD,size_t size)164 static void *RxMTMalloc( const RxPackageGlobalDataDef *TSD, size_t size )
165 {
166    mt_tsd_t *mt;
167    MT_mem *new;
168 
169    if (DosAllocMem((PPVOID) &new,
170                    size + sizeof(MT_mem),
171                    PAG_READ|PAG_WRITE|PAG_COMMIT) != NO_ERROR)
172       return(NULL); /* may happen. errors are detected in the above layers */
173 
174    mt = TSD->mt_tsd;
175    new->prev = NULL;
176    new->next = mt->mem_base;
177    if (mt->mem_base)
178       mt->mem_base->prev = new;
179    mt->mem_base = new;
180    return(new + 1); /* jump over the head */
181 }
182 
183 /* Lowest level memory deallocation function for normal circumstances. */
RxMTFree(const RxPackageGlobalDataDef * TSD,void * chunk)184 static void RxMTFree( const RxPackageGlobalDataDef *TSD, void *chunk )
185 {
186    mt_tsd_t *mt = TSD->mt_tsd;
187    MT_mem *this;
188 
189    /*
190     * Just in case...
191     */
192    if (chunk == NULL)
193       return;
194 
195    this = chunk;
196    this--; /* Go to the header of the chunk */
197 
198    if (this->prev)
199       if (this->prev->next != this)
200          return;
201    if (this->next)
202       if (this->next->prev != this)
203          return;
204 
205    /* This is a chunk allocated by RxMTMalloc */
206    if (this->prev)
207       this->prev->next = this->next;
208    if (this->next)
209       this->next->prev = this->prev;
210    if (this == mt->mem_base)
211       mt->mem_base = this->next;
212 
213    /* Last not least we set the pointers to NULL. This prevents a double-free*/
214    this->next = NULL;
215    this->prev = NULL;
216    DosFreeMem(this);
217 }
218 
219 /* Lowest level exit handler. */
RxMTExit(int code)220 static void RxMTExit(int code)
221 {
222    DosExit(EXIT_THREAD,code);
223 }
224 
225 /* RxPackInitializeThread creates a new thread structure and returns a ptr
226  * to the initialized value.
227  * The function may be called more than once.
228  */
RxPackInitializeThread(void)229 RxPackageGlobalDataDef *RxPackInitializeThread(void)
230 {
231    RxPackageGlobalDataDef *retval,**ptsd;
232    mt_tsd_t *mt;
233 
234    /* If you run into trouble here, you must change the code in
235     * ReginsSetMutex/RxPackUnsetMutex. The argument there assumes the
236     * following rule. This is an ugly hack.
237     */
238    assert(sizeof_ptr() >= sizeof(HMTX));
239    if (sizeof_ptr() < sizeof(HMTX))
240       return(NULL); /* Be absolutely sure that we HAVE a problem */
241 
242    ptsd = FindThreadDataIdx();
243    if (ptsd == NULL) /* can't initialize? */
244       return(NULL);
245 
246    if (*ptsd != NULL) /* already initialized? */
247       return(*ptsd);
248 
249    /* First call in this thread, a atexit() per thread will be great, sigh...*/
250    retval = malloc(sizeof(RxPackageGlobalDataDef)); /* no Malloc, etc! */
251 
252    if (retval == NULL) /* THIS is really a problem. I don't know what we */
253       return(NULL);    /* should do now. Let the caller run into a crash... */
254 
255    *ptsd = retval;
256 
257    memset(retval,0,sizeof(RxPackageGlobalDataDef));
258    retval->RxMTMalloc = (void *)RxMTMalloc;
259    retval->RxMTFree = (void *)RxMTFree;
260    retval->RxMTExit = (void *)RxMTExit;
261 
262    /* Since the local data structure contains a Heap object for the memory
263     * management we initialize it first.
264     */
265    if ((mt = malloc(sizeof(mt_tsd_t))) == NULL)
266       return(NULL);                     /* This is a catastrophy             */
267    retval->mt_tsd = mt;
268    mt->mem_base = NULL;
269 
270    if ( !INIT_RXPACKAGE( retval ) )
271       return(NULL);
272 
273 #if defined(__WATCOMC__)
274    retval->thread_id = *_threadid;
275 #else
276    retval->thread_id = _gettid();
277 #endif
278 
279    return(retval);
280 }
281 
282 /* __rxpack_get_tsd returns a pointer to the thread specific data. Be sure to
283  * calls this after a RxPackInitializeThread only.
284  */
__rxpack_get_tsd(void)285 RxPackageGlobalDataDef *__rxpack_get_tsd(void)
286 {
287    /* See above for comments */
288    return(*FindThreadDataIdx());
289 }
290 
291 /* RxPackSetMutex is the opposite of RxPackUnsetMutex and sets a mutex
292  * variable. The "true" mutex is "*arg" since we have hidden the type
293  * HMTX which is the correct type. Thus, we have used "HMTX" and
294  * "void *" in the same manner. If we include windows.h for the
295  * definition of HANDLE we cant include windows later and may run
296  * into trouble. The initialization code will check of errors of
297  * this assumption.
298  * The argument (*mutex) may be NULL. We initialize the mutex in this
299  * case. This prevents the calling functions to initialize the mutex.
300  * The is a little speed penalty but the mutexes are not used very
301  * often. YOU should change it if it hurts you.
302  */
RxPackSetMutex(void ** mutex)303 void RxPackSetMutex(void **mutex)
304 {
305    int OK = 1;
306    HMTX *os2_mutex = (HMTX *) mutex;
307 
308    if (*os2_mutex == (HMTX) 0)
309    {
310       DosEnterCritSec();
311       if (*os2_mutex == (HMTX) 0) /* may have changed due MT */
312       {
313          if (DosCreateMutexSem(NULL,os2_mutex,0,FALSE) != NO_ERROR)
314             OK = 0;
315       }
316       DosExitCritSec();
317       if (!OK)
318       { /* We must die now! There is no other chance. */
319          *((int *) NULL) = 1;
320       }
321    }
322 
323    DosRequestMutexSem(*os2_mutex,SEM_INDEFINITE_WAIT);
324    /* ignore errors, we continue especially if ERROR_INTERRUPTED occurs.
325     * FIXME, ignoring ERROR_INTERRUPTED OK?
326     */
327 }
328 
329 /* see RxPackSetMutex */
RxPackUnsetMutex(void ** mutex)330 void RxPackUnsetMutex(void **mutex)
331 {
332    HMTX *os2_mutex = (HMTX *) mutex;
333 
334    DosReleaseMutexSem(*os2_mutex);
335 }
336 
337 /* get our thread id */
RxPackGetThreadID(void)338 unsigned long RxPackGetThreadID(void)
339 {
340 #if defined(__WATCOMC__)
341    return (unsigned long)*_threadid;
342 #else
343    return (unsigned long)_gettid();
344 #endif
345 }
346