reenabled swaptest. quake should now load data and start on big endian architectures...
[AROS-Contrib.git] / regina / mt_os2.c
blob4c074a38cfd969ff14eb4f7ea870b5be6a45ce25
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 #elif defined (__WATCOMC__)
19 # define DONT_TYPEDEF_PFN
20 # include <os2.h>
21 #endif
23 #include "rexx.h"
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
30 static ULONG *TSD_ptrs = NULL;
32 typedef struct _MT_mem {
33 struct _MT_mem *prev;
34 struct _MT_mem *next;
35 /* real data follows here */
36 } MT_mem;
38 typedef struct { /* mt_tsd: static variables of this module (thread-safe) */
39 MT_mem *mem_base; /* From DosAllocMem, we use this memory scheme since it is
40 * native OS/2. emx has its own heap allocation functions.
42 } mt_tsd_t; /* thread-specific but only needed by this module. see
43 * ReginaInitializeThread
46 /* Returns a pointer to the TSD pointer */
47 static tsd_t **FindThreadDataIdx(void)
49 tsd_t **retval = NULL;
50 ULONG rc;
52 DosEnterCritSec();
53 if (TSD_ptrs != NULL)
55 retval = (tsd_t **) TSD_ptrs;
56 goto Leave;
58 if ((rc = DosAllocThreadLocalMemory(1,&TSD_ptrs)) != NO_ERROR)
60 fprintf(stderr,"rc = %lu from DosAllocThreadLocalMemory, await SIGSEGV!\n",
61 (unsigned long) rc);
62 TSD_ptrs = NULL;
64 if (TSD_ptrs != NULL)
65 retval = (tsd_t **) TSD_ptrs;
66 Leave:
67 DosExitCritSec();
68 return(retval);
71 static void Deinitialize(void *buf)
73 tsd_t *TSD = buf;
74 mt_tsd_t *mt;
75 MT_mem *chunk;
77 if (TSD == NULL) /* Should never happen but be sure */
78 return;
80 deinit_rexxsaa(TSD);
82 mt = TSD->mt_tsd;
83 if (mt)
84 while ((chunk = mt->mem_base) != NULL)
86 TSD->MTFree(TSD,chunk + 1);
87 if (mt->mem_base == chunk)
88 break; /* something goes wrong. Don't run into an endless loop */
90 if (mt)
91 free(mt);
92 free(TSD);
95 int IfcReginaCleanup( VOID )
97 tsd_t **ptsd = FindThreadDataIdx();
99 if (*ptsd == NULL)
100 return 0;
101 Deinitialize(*ptsd);
102 *ptsd = NULL;
104 return 1;
107 #ifdef DYNAMIC
108 /* Deinitialize is called when the thread terminates.
109 * This is a wonderful position to place code which frees all allocated stuff!
111 #ifdef __EMX__
112 /* We provide a DLL entry function. Look at the standard documentation for
113 * EMX. See emxdev.doc and testdll6.c. We may use the macros CRT_INIT1 and
114 * CRT_TERM1 from sys/startup.h instead but that's undocumented stuff.
116 int _CRT_init (void);
117 void _CRT_term (void);
118 void __ctordtorInit (void);
119 void __ctordtorTerm (void);
121 unsigned long _DLL_InitTerm (unsigned long mod_handle, unsigned long flag)
123 switch (flag)
125 case 0:
126 if (_CRT_init() != 0)
127 return 0;
128 __ctordtorInit();
129 return 1;
130 case 1:
132 * This will run ONLY if called on dynamic unload of the complete
133 * library. This means, the last thread will receive an unload
134 * command. Stupid OS/2.
136 IfcReginaCleanup();
137 __ctordtorTerm();
138 _CRT_term();
139 return 1;
140 default:
141 break;
143 return 0;
145 #endif /* EMX */
146 #endif /* DYNAMIC */
148 /* This should prevent some error messages and is used as a #define */
149 static unsigned sizeof_ptr(void)
151 return(sizeof(void *));
154 /* Lowest level memory allocation function for normal circumstances. */
155 static void *MTMalloc( const tsd_t *TSD, size_t size )
157 mt_tsd_t *mt;
158 MT_mem *new;
160 if (DosAllocMem((PPVOID) &new,
161 size + sizeof(MT_mem),
162 PAG_READ|PAG_WRITE|PAG_COMMIT) != NO_ERROR)
163 return(NULL); /* may happen. errors are detected in the above layers */
165 mt = TSD->mt_tsd;
166 new->prev = NULL;
167 new->next = mt->mem_base;
168 if (mt->mem_base)
169 mt->mem_base->prev = new;
170 mt->mem_base = new;
171 return(new + 1); /* jump over the head */
174 /* Lowest level memory deallocation function for normal circumstances. */
175 static void MTFree( const tsd_t *TSD, void *chunk )
177 mt_tsd_t *mt = TSD->mt_tsd;
178 MT_mem *this;
181 * Just in case...
183 if (chunk == NULL)
184 return;
186 this = chunk;
187 this--; /* Go to the header of the chunk */
189 if (this->prev)
190 if (this->prev->next != this)
191 return;
192 if (this->next)
193 if (this->next->prev != this)
194 return;
196 /* This is a chunk allocated by MTMalloc */
197 if (this->prev)
198 this->prev->next = this->next;
199 if (this->next)
200 this->next->prev = this->prev;
201 if (this == mt->mem_base)
202 mt->mem_base = this->next;
204 /* Last not least we set the pointers to NULL. This prevents a double-free*/
205 this->next = NULL;
206 this->prev = NULL;
207 DosFreeMem(this);
210 /* Lowest level exit handler. */
211 static void MTExit(int code)
213 DosExit(EXIT_THREAD,code);
216 /* ReginaInitializeThread creates a new thread structure and returns a ptr
217 * to the initialized value.
218 * The function may be called more than once.
220 tsd_t *ReginaInitializeThread(void)
222 int OK;
223 tsd_t *retval,**ptsd;
224 mt_tsd_t *mt;
226 /* If you run into trouble here, you must change the code in
227 * ReginsSetMutex/ReginaUnsetMutex. The argument there assumes the
228 * following rule. This is an ugly hack.
230 assert(sizeof_ptr() >= sizeof(HMTX));
231 if (sizeof_ptr() < sizeof(HMTX))
232 return(NULL); /* Be absolutely sure that we HAVE a problem */
234 ptsd = FindThreadDataIdx();
235 if (ptsd == NULL) /* can't initialize? */
236 return(NULL);
238 if (*ptsd != NULL) /* already initialized? */
239 return(*ptsd);
241 /* First call in this thread, a atexit() per thread will be great, sigh...*/
242 retval = malloc(sizeof(tsd_t)); /* no Malloc, etc! */
244 if (retval == NULL) /* THIS is really a problem. I don't know what we */
245 return(NULL); /* should do now. Let the caller run into a crash... */
247 *ptsd = retval;
249 memset(retval,0,sizeof(tsd_t));
250 retval->MTMalloc = MTMalloc;
251 retval->MTFree = MTFree;
252 retval->MTExit = MTExit;
254 /* Since the local data structure contains a Heap object for the memory
255 * management we initialize it first.
257 if ((mt = malloc(sizeof(mt_tsd_t))) == NULL)
258 return(NULL); /* This is a catastrophy */
259 retval->mt_tsd = mt;
260 mt->mem_base = NULL;
262 OK = init_memory(retval); /* Initialize the memory module FIRST*/
264 /* Without the initial memory we don't have ANY chance! */
265 if (!OK)
266 return(NULL);
269 extern OS_Dep_funcs __regina_OS_Os2;
270 retval->OS = &__regina_OS_Os2;
273 OK &= init_vars(retval); /* Initialize the variable module */
274 OK &= init_stacks(retval); /* Initialize the stack module */
275 OK &= init_filetable(retval); /* Initialize the files module */
276 OK &= init_math(retval); /* Initialize the math module */
277 OK &= init_spec_vars(retval); /* Initialize the interprt module */
278 OK &= init_tracing(retval); /* Initialize the tracing module */
279 OK &= init_builtin(retval); /* Initialize the builtin module */
280 OK &= init_client(retval); /* Initialize the client module */
281 OK &= init_library(retval); /* Initialize the library module */
282 OK &= init_rexxsaa(retval); /* Initialize the rexxsaa module */
283 OK &= init_shell(retval); /* Initialize the shell module */
284 OK &= init_envir(retval); /* Initialize the envir module */
285 OK &= init_expr(retval); /* Initialize the expr module */
286 OK &= init_error(retval); /* Initialize the error module */
287 #ifdef VMS
288 OK &= init_vms(retval); /* Initialize the vmscmd module */
289 OK &= init_vmf(retval); /* Initialize the vmsfuncs module */
290 #endif
291 OK &= init_arexxf(retval); /* Initialize the arxfuncs modules */
292 retval->loopcnt = 1; /* stupid r2perl-module */
293 retval->traceparse = -1;
294 #ifdef __EMX__
295 retval->thread_id = _gettid();
296 #elif defined(__WATCOMC__)
297 retval->thread_id = *_threadid;
298 #else
299 retval->thread_id = _gettid();
300 #endif
302 if (!OK)
303 exiterror( ERR_STORAGE_EXHAUSTED, 0 ) ;
304 return(retval);
307 /* __regina_get_tsd returns a pointer to the thread specific data. Be sure to
308 * calls this after a ReginaInitializeThread only.
310 tsd_t *__regina_get_tsd(void)
312 /* See above for comments */
313 return(*FindThreadDataIdx());
316 /* ReginaSetMutex is the opposite of ReginaUnsetMutex and sets a mutex
317 * variable. The "true" mutex is "*arg" since we have hidden the type
318 * HMTX which is the correct type. Thus, we have used "HMTX" and
319 * "void *" in the same manner. If we include windows.h for the
320 * definition of HANDLE we cant include windows later and may run
321 * into trouble. The initialization code will check of errors of
322 * this assumption.
323 * The argument (*mutex) may be NULL. We initialize the mutex in this
324 * case. This prevents the calling functions to initialize the mutex.
325 * The is a little speed penalty but the mutexes are not used very
326 * often. YOU should change it if it hurts you.
328 void ReginaSetMutex(void **mutex)
330 int OK = 1;
331 HMTX *os2_mutex = (HMTX *) mutex;
333 if (*os2_mutex == (HMTX) 0)
335 DosEnterCritSec();
336 if (*os2_mutex == (HMTX) 0) /* may have changed due MT */
338 if (DosCreateMutexSem(NULL,os2_mutex,0,FALSE) != NO_ERROR)
339 OK = 0;
341 DosExitCritSec();
342 if (!OK)
343 { /* We must die now! There is no other chance. */
344 *((int *) NULL) = 1;
348 DosRequestMutexSem(*os2_mutex,SEM_INDEFINITE_WAIT);
349 /* ignore errors, we continue especially if ERROR_INTERRUPTED occurs.
350 * FIXME, ignoring ERROR_INTERRUPTED OK?
354 /* see ReginaSetMutex */
355 void ReginaUnsetMutex(void **mutex)
357 HMTX *os2_mutex = (HMTX *) mutex;
359 DosReleaseMutexSem(*os2_mutex);