bringing SDL 1.2.14 from vendor into the main branch
[AROS-Contrib.git] / regina / mt_os2.c
blobfd7ae5745638ab5bd0f60529cc66e2b6001eec0b
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 */
8 #define INCL_NOCOMMON
9 #define INCL_DOSPROCESS
10 #define INCL_DOSSEMAPHORES
11 #define INCL_DOSERRORS
12 #define INCL_DOSMEMMGR
14 #ifdef __EMX__
15 # define DONT_TYPEDEF_PFN
16 # include <io.h>
17 # include <os2emx.h>
18 #endif
20 #include "rexx.h"
21 #include <stdlib.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <assert.h>
27 static ULONG *TSD_ptrs = NULL;
29 typedef struct _MT_mem {
30 struct _MT_mem *prev;
31 struct _MT_mem *next;
32 /* real data follows here */
33 } MT_mem;
35 typedef struct { /* mt_tsd: static variables of this module (thread-safe) */
36 MT_mem *mem_base; /* From DosAllocMem, we use this memory scheme since it is
37 * native OS/2. emx has its own heap allocation functions.
39 } mt_tsd_t; /* thread-specific but only needed by this module. see
40 * ReginaInitializeThread
43 /* Returns a pointer to the TSD pointer */
44 static tsd_t **FindThreadDataIdx(void)
46 tsd_t **retval = NULL;
47 ULONG rc;
49 DosEnterCritSec();
50 if (TSD_ptrs != NULL)
52 retval = (tsd_t **) TSD_ptrs;
53 goto Leave;
55 if ((rc = DosAllocThreadLocalMemory(1,&TSD_ptrs)) != NO_ERROR)
57 fprintf(stderr,"rc = %lu from DosAllocThreadLocalMemory, await SIGSEGV!\n",
58 (unsigned long) rc);
59 TSD_ptrs = NULL;
61 if (TSD_ptrs != NULL)
62 retval = (tsd_t **) TSD_ptrs;
63 Leave:
64 DosExitCritSec();
65 return(retval);
68 #ifdef DYNAMIC
69 /* Deinitialize is called when the thread terminates.
70 * This is a wonderful position to place code which frees all allocated stuff!
72 static void Deinitialize(void *buf)
74 tsd_t *TSD = buf;
75 mt_tsd_t *mt;
76 MT_mem *chunk;
78 if (TSD == NULL) /* Should never happen but be sure */
79 return;
81 deinit_rexxsaa(TSD);
83 mt = TSD->mt_tsd;
84 if (mt)
85 while ((chunk = mt->mem_base) != NULL)
87 TSD->MTFree(TSD,chunk + 1);
88 if (mt->mem_base == chunk)
89 break; /* something goes wrong. Don't run into an endless loop */
91 if (mt)
92 free(mt);
93 free(TSD);
96 #ifdef __EMX__
97 /* We provide a DLL entry function. Look at the standard documentation for
98 * EMX. See emxdev.doc and testdll6.c. We may use the macros CRT_INIT1 and
99 * CRT_TERM1 from sys/startup.h instead but that's undocumented stuff.
101 int _CRT_init (void);
102 void _CRT_term (void);
103 void __ctordtorInit (void);
104 void __ctordtorTerm (void);
106 unsigned long _DLL_InitTerm (unsigned long mod_handle, unsigned long flag)
108 switch (flag)
110 case 0:
111 if (_CRT_init () != 0)
112 return 0;
113 __ctordtorInit ();
114 printf("\nThe regina DLL is initializing. Check if per thread!\n\n");
115 return 1;
116 case 1:
117 /* This will run ONLY if called per thread */
118 #if 0
120 tsd_t *TSD;
121 TSD = __regina_get_tsd();
122 if (TSD != NULL)
124 deinit_rexxsaa(TSD);
125 Deinitialize(TSD);
128 #endif
129 printf("\nThe regina DLL is deinitializing. Check if per thread!\n\n");
130 __ctordtorTerm ();
131 _CRT_term ();
132 return 1;
133 default:
134 break;
136 return 0;
138 #endif /* EMX */
139 #endif /* DYNAMIC */
141 /* This should prevent some error messages and is used as a #define */
142 static unsigned sizeof_ptr(void)
144 return(sizeof(void *));
147 /* Lowest level memory allocation function for normal circumstances. */
148 static void *MTMalloc(const tsd_t *TSD,size_t size)
150 mt_tsd_t *mt;
151 MT_mem *new;
153 if (DosAllocMem((PPVOID) &new,
154 size + sizeof(MT_mem),
155 PAG_READ|PAG_WRITE|PAG_COMMIT) != NO_ERROR)
156 return(NULL); /* may happen. errors are detected in the above layers */
158 mt = TSD->mt_tsd;
159 new->prev = NULL;
160 new->next = mt->mem_base;
161 if (mt->mem_base)
162 mt->mem_base->prev = new;
163 mt->mem_base = new;
164 return(new + 1); /* jump over the head */
167 /* Lowest level memory deallocation function for normal circumstances. */
168 static void MTFree(const tsd_t *TSD,void *chunk)
170 mt_tsd_t *mt = TSD->mt_tsd;
171 MT_mem *this;
173 this = chunk;
174 this--; /* Go to the header of the chunk */
176 if (this->prev)
177 if (this->prev->next != this)
178 return;
179 if (this->next)
180 if (this->next->prev != this)
181 return;
183 /* This is a chunk allocated by MTMalloc */
184 if (this->prev)
185 this->prev->next = this->next;
186 if (this->next)
187 this->next->prev = this->prev;
188 if (this == mt->mem_base)
189 mt->mem_base = this->next;
191 /* Last not least we set the pointers to NULL. This prevents a double-free*/
192 this->next = NULL;
193 this->prev = NULL;
194 DosFreeMem(this);
197 /* Lowest level exit handler. */
198 static void MTExit(int code)
200 DosExit(EXIT_THREAD,code);
203 /* ReginaInitializeThread creates a new thread structure and returns a ptr
204 * to the initialized value.
205 * The function may be called more than once.
207 tsd_t *ReginaInitializeThread(void)
209 int OK;
210 tsd_t *retval,**ptsd;
211 mt_tsd_t *mt;
213 /* If you run into trouble here, you must change the code in
214 * ReginsSetMutex/ReginaUnsetMutex. The argument there assumes the
215 * following rule. This is an ugly hack.
217 assert(sizeof_ptr() >= sizeof(HMTX));
218 if (sizeof_ptr() < sizeof(HMTX))
219 return(NULL); /* Be absolutely sure that we HAVE a problem */
221 ptsd = FindThreadDataIdx();
222 if (ptsd == NULL) /* can't initialize? */
223 return(NULL);
225 if (*ptsd != NULL) /* already initialized? */
226 return(*ptsd);
228 /* First call in this thread, a atexit() per thread will be great, sigh...*/
229 retval = malloc(sizeof(tsd_t)); /* no Malloc, etc! */
231 if (retval == NULL) /* THIS is really a problem. I don't know what we */
232 return(NULL); /* should do now. Let the caller run into a crash... */
234 *ptsd = retval;
236 memset(retval,0,sizeof(tsd_t));
237 retval->MTMalloc = MTMalloc;
238 retval->MTFree = MTFree;
239 retval->MTExit = MTExit;
241 /* Since the local data structure contains a Heap object for the memory
242 * management we initialize it first.
244 if ((mt = malloc(sizeof(mt_tsd_t))) == NULL)
245 return(NULL); /* This is a catastrophy */
246 retval->mt_tsd = mt;
247 mt->mem_base = NULL;
249 OK = init_memory(retval); /* Initialize the memory module FIRST*/
251 /* Without the initial memory we don't have ANY chance! */
252 if (!OK)
253 return(NULL);
255 OK &= init_vars(retval); /* Initialize the variable module */
256 OK &= init_stacks(retval); /* Initialize the stack module */
257 OK &= init_filetable(retval); /* Initialize the files module */
258 OK &= init_math(retval); /* Initialize the math module */
259 OK &= init_spec_vars(retval); /* Initialize the interprt module */
260 OK &= init_tracing(retval); /* Initialize the tracing module */
261 OK &= init_builtin(retval); /* Initialize the builtin module */
262 OK &= init_client(retval); /* Initialize the client module */
263 OK &= init_library(retval); /* Initialize the library module */
264 OK &= init_rexxsaa(retval); /* Initialize the rexxsaa module */
265 OK &= init_shell(retval); /* Initialize the shell module */
266 OK &= init_envir(retval); /* Initialize the envir module */
267 OK &= init_expr(retval); /* Initialize the expr module */
268 OK &= init_error(retval); /* Initialize the error module */
269 #ifdef VMS
270 OK &= init_vms(retval); /* Initialize the vmscmd module */
271 OK &= init_vmf(retval); /* Initialize the vmsfuncs module */
272 #endif
273 OK |= init_arexxf(&__regina_tsd); /* Initialize the arexxfuncs modules */
274 retval->loopcnt = 1; /* stupid r2perl-module */
275 retval->traceparse = -1;
276 retval->thread_id = _gettid();
278 if (!OK)
279 exiterror( ERR_STORAGE_EXHAUSTED, 0 ) ;
280 return(retval);
283 /* __regina_get_tsd returns a pointer to the thread specific data. Be sure to
284 * calls this after a ReginaInitializeThread only.
286 tsd_t *__regina_get_tsd(void)
288 /* See above for comments */
289 return(*FindThreadDataIdx());
292 /* ReginaSetMutex is the opposite of ReginaUnsetMutex and sets a mutex
293 * variable. The "true" mutex is "*arg" since we have hidden the type
294 * HMTX which is the correct type. Thus, we have used "HMTX" and
295 * "void *" in the same manner. If we include windows.h for the
296 * definition of HANDLE we cant include windows later and may run
297 * into trouble. The initialization code will check of errors of
298 * this assumption.
299 * The argument (*mutex) may be NULL. We initialize the mutex in this
300 * case. This prevents the calling functions to initialize the mutex.
301 * The is a little speed penalty but the mutexes are not used very
302 * often. YOU should change it if it hurts you.
304 void ReginaSetMutex(void **mutex)
306 int OK = 1;
307 HMTX *os2_mutex = (HMTX *) mutex;
309 if (*os2_mutex == (HMTX) 0)
311 DosEnterCritSec();
312 if (*os2_mutex == (HMTX) 0) /* may have changed due MT */
314 if (DosCreateMutexSem(NULL,os2_mutex,0,FALSE) != NO_ERROR)
315 OK = 0;
317 DosExitCritSec();
318 if (!OK)
319 { /* We must die now! There is no other chance. */
320 *((int *) NULL) = 1;
324 DosRequestMutexSem(*os2_mutex,SEM_INDEFINITE_WAIT);
325 /* ignore errors, we continue especially if ERROR_INTERRUPTED occurs.
326 * FIXME, ignoring ERROR_INTERRUPTED OK?
330 /* see ReginaSetMutex */
331 void ReginaUnsetMutex(void **mutex)
333 HMTX *os2_mutex = (HMTX *) mutex;
335 DosReleaseMutexSem(*os2_mutex);