Upload Tizen:Base source
[toolchains/nspr.git] / mozilla / nsprpub / pr / src / md / unix / unixware.c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is the Netscape Portable Runtime (NSPR).
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-2000
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 #include "primpl.h"
39
40 #if !defined (USE_SVR4_THREADS)
41
42 /*
43          * using only NSPR threads here
44  */
45
46 #include <setjmp.h>
47
48 void _MD_EarlyInit(void)
49 {
50 }
51
52 PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
53 {
54     if (isCurrent) {
55         (void) setjmp(CONTEXT(t));
56     }
57     *np = sizeof(CONTEXT(t)) / sizeof(PRWord);
58     return (PRWord *) CONTEXT(t);
59 }
60
61 #ifdef ALARMS_BREAK_TCP /* I don't think they do */
62
63 PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen,
64                         PRIntervalTime timeout)
65 {
66     PRInt32 rv;
67
68     _MD_BLOCK_CLOCK_INTERRUPTS();
69     rv = _connect(osfd,addr,addrlen);
70     _MD_UNBLOCK_CLOCK_INTERRUPTS();
71 }
72
73 PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen,
74                         PRIntervalTime timeout)
75 {
76     PRInt32 rv;
77
78     _MD_BLOCK_CLOCK_INTERRUPTS();
79     rv = _accept(osfd,addr,addrlen);
80     _MD_UNBLOCK_CLOCK_INTERRUPTS();
81     return(rv);
82 }
83 #endif
84
85 /*
86  * These are also implemented in pratom.c using NSPR locks.  Any reason
87  * this might be better or worse?  If you like this better, define
88  * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h
89  */
90 #ifdef _PR_HAVE_ATOMIC_OPS
91 /* Atomic operations */
92 #include  <stdio.h>
93 static FILE *_uw_semf;
94
95 void
96 _MD_INIT_ATOMIC(void)
97 {
98     /* Sigh.  Sure wish SYSV semaphores weren't such a pain to use */
99     if ((_uw_semf = tmpfile()) == NULL)
100         PR_ASSERT(0);
101
102     return;
103 }
104
105 void
106 _MD_ATOMIC_INCREMENT(PRInt32 *val)
107 {
108     flockfile(_uw_semf);
109     (*val)++;
110     unflockfile(_uw_semf);
111 }
112
113 void
114 _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
115 {
116     flockfile(_uw_semf);
117     (*ptr) += val;
118     unflockfile(_uw_semf);
119 }
120
121 void
122 _MD_ATOMIC_DECREMENT(PRInt32 *val)
123 {
124     flockfile(_uw_semf);
125     (*val)--;
126     unflockfile(_uw_semf);
127 }
128
129 void
130 _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
131 {
132     flockfile(_uw_semf);
133     *val = newval;
134     unflockfile(_uw_semf);
135 }
136 #endif
137
138 void
139 _MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri)
140 {
141     return;
142 }
143
144 PRStatus
145 _MD_InitializeThread(PRThread *thread)
146 {
147         return PR_SUCCESS;
148 }
149
150 PRStatus
151 _MD_WAIT(PRThread *thread, PRIntervalTime ticks)
152 {
153     PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
154     _PR_MD_SWITCH_CONTEXT(thread);
155     return PR_SUCCESS;
156 }
157
158 PRStatus
159 _MD_WAKEUP_WAITER(PRThread *thread)
160 {
161     if (thread) {
162         PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE));
163     }
164     return PR_SUCCESS;
165 }
166
167 /* These functions should not be called for Unixware */
168 void
169 _MD_YIELD(void)
170 {
171     PR_NOT_REACHED("_MD_YIELD should not be called for Unixware.");
172 }
173
174 PRStatus
175 _MD_CREATE_THREAD(
176     PRThread *thread,
177     void (*start) (void *),
178     PRThreadPriority priority,
179     PRThreadScope scope,
180     PRThreadState state,
181     PRUint32 stackSize)
182 {
183     PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware.");
184 }
185
186 #else  /* USE_SVR4_THREADS */
187
188 /* NOTE:
189  * SPARC v9 (Ultras) do have an atomic test-and-set operation.  But
190  * SPARC v8 doesn't.  We should detect in the init if we are running on
191  * v8 or v9, and then use assembly where we can.
192  */
193
194 #include <thread.h>
195 #include <synch.h>
196
197 static mutex_t _unixware_atomic = DEFAULTMUTEX;
198
199 #define TEST_THEN_ADD(where, inc) \
200     if (mutex_lock(&_unixware_atomic) != 0)\
201         PR_ASSERT(0);\
202     *where += inc;\
203     if (mutex_unlock(&_unixware_atomic) != 0)\
204         PR_ASSERT(0);
205
206 #define TEST_THEN_SET(where, val) \
207     if (mutex_lock(&_unixware_atomic) != 0)\
208         PR_ASSERT(0);\
209     *where = val;\
210     if (mutex_unlock(&_unixware_atomic) != 0)\
211         PR_ASSERT(0);
212
213 void
214 _MD_INIT_ATOMIC(void)
215 {
216 }
217
218 void
219 _MD_ATOMIC_INCREMENT(PRInt32 *val)
220 {
221     TEST_THEN_ADD(val, 1);
222 }
223
224 void
225 _MD_ATOMIC_ADD(PRInt32 *ptr, PRInt32 val)
226 {
227     TEST_THEN_ADD(ptr, val);
228 }
229
230 void
231 _MD_ATOMIC_DECREMENT(PRInt32 *val)
232 {
233     TEST_THEN_ADD(val, 0xffffffff);
234 }
235
236 void
237 _MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval)
238 {
239     TEST_THEN_SET(val, newval);
240 }
241
242 #include <signal.h>
243 #include <errno.h>
244 #include <fcntl.h>
245
246 #include <sys/lwp.h>
247 #include <sys/procfs.h>
248 #include <sys/syscall.h>
249
250
251 THREAD_KEY_T threadid_key;
252 THREAD_KEY_T cpuid_key;
253 THREAD_KEY_T last_thread_key;
254 static sigset_t set, oldset;
255
256 void _MD_EarlyInit(void)
257 {
258     THR_KEYCREATE(&threadid_key, NULL);
259     THR_KEYCREATE(&cpuid_key, NULL);
260     THR_KEYCREATE(&last_thread_key, NULL);
261     sigemptyset(&set);
262     sigaddset(&set, SIGALRM);
263 }
264
265 PRStatus _MD_CREATE_THREAD(PRThread *thread, 
266                                         void (*start)(void *), 
267                                         PRThreadPriority priority,
268                                         PRThreadScope scope, 
269                                         PRThreadState state, 
270                                         PRUint32 stackSize) 
271 {
272         long flags;
273         
274     /* mask out SIGALRM for native thread creation */
275     thr_sigsetmask(SIG_BLOCK, &set, &oldset); 
276
277     flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/ 
278                            : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/);
279         if (_PR_IS_GCABLE_THREAD(thread) ||
280                                                         (scope == PR_GLOBAL_BOUND_THREAD))
281                 flags |= THR_BOUND;
282
283     if (thr_create(NULL, thread->stack->stackSize,
284                   (void *(*)(void *)) start, (void *) thread, 
285                                   flags,
286                   &thread->md.handle)) {
287         thr_sigsetmask(SIG_SETMASK, &oldset, NULL); 
288         return PR_FAILURE;
289     }
290
291
292     /* When the thread starts running, then the lwpid is set to the right
293      * value. Until then we want to mark this as 'uninit' so that
294      * its register state is initialized properly for GC */
295
296     thread->md.lwpid = -1;
297     thr_sigsetmask(SIG_SETMASK, &oldset, NULL); 
298     _MD_NEW_SEM(&thread->md.waiter_sem, 0);
299
300         if ((scope == PR_GLOBAL_THREAD) || (scope == PR_GLOBAL_BOUND_THREAD)) {
301                 thread->flags |= _PR_GLOBAL_SCOPE;
302     }
303
304     /* 
305     ** Set the thread priority.  This will also place the thread on 
306     ** the runQ.
307     **
308     ** Force PR_SetThreadPriority to set the priority by
309     ** setting thread->priority to 100.
310     */
311     {
312     int pri;
313     pri = thread->priority;
314     thread->priority = 100;
315     PR_SetThreadPriority( thread, pri );
316
317     PR_LOG(_pr_thread_lm, PR_LOG_MIN, 
318             ("(0X%x)[Start]: on to runq at priority %d",
319             thread, thread->priority));
320     }
321
322     /* Activate the thread */
323     if (thr_continue( thread->md.handle ) ) {
324         return PR_FAILURE;
325     }
326     return PR_SUCCESS;
327 }
328
329 void _MD_cleanup_thread(PRThread *thread)
330 {
331     thread_t hdl;
332     PRMonitor *mon;
333
334     hdl = thread->md.handle;
335
336     /* 
337     ** First, suspend the thread (unless it's the active one)
338     ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to
339     ** prevent both of us modifying the thread structure at the same time.
340     */
341     if ( thread != _PR_MD_CURRENT_THREAD() ) {
342         thr_suspend(hdl);
343     }
344     PR_LOG(_pr_thread_lm, PR_LOG_MIN,
345             ("(0X%x)[DestroyThread]\n", thread));
346
347     _MD_DESTROY_SEM(&thread->md.waiter_sem);
348 }
349
350 void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri)
351 {
352         if(thr_setprio((thread_t)md_thread->handle, newPri)) {
353                 PR_LOG(_pr_thread_lm, PR_LOG_MIN,
354                    ("_PR_SetThreadPriority: can't set thread priority\n"));
355         }
356 }
357
358 void _MD_WAIT_CV(
359     struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout)
360 {
361     struct timespec tt;
362     PRUint32 msec;
363     int rv;
364     PRThread *me = _PR_MD_CURRENT_THREAD();
365
366     msec = PR_IntervalToMilliseconds(timeout);
367
368     GETTIME (&tt);
369
370     tt.tv_sec += msec / PR_MSEC_PER_SEC;
371     tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC;
372     /* Check for nsec overflow - otherwise we'll get an EINVAL */
373     if (tt.tv_nsec >= PR_NSEC_PER_SEC) {
374         tt.tv_sec++;
375         tt.tv_nsec -= PR_NSEC_PER_SEC;
376     }
377     me->md.sp = unixware_getsp();
378
379
380     /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason
381      * hence ignore EINTR for now */
382
383     COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt);
384 }
385
386 void _MD_lock(struct _MDLock *md_lock)
387 {
388     mutex_lock(&md_lock->lock);
389 }
390
391 void _MD_unlock(struct _MDLock *md_lock)
392 {
393     mutex_unlock(&((md_lock)->lock));
394 }
395
396
397 PRThread *_pr_current_thread_tls()
398 {
399     PRThread *ret;
400
401     thr_getspecific(threadid_key, (void **)&ret);
402     return ret;
403 }
404
405 PRStatus
406 _MD_WAIT(PRThread *thread, PRIntervalTime ticks)
407 {
408         _MD_WAIT_SEM(&thread->md.waiter_sem);
409         return PR_SUCCESS;
410 }
411
412 PRStatus
413 _MD_WAKEUP_WAITER(PRThread *thread)
414 {
415         if (thread == NULL) {
416                 return PR_SUCCESS;
417         }
418         _MD_POST_SEM(&thread->md.waiter_sem);
419         return PR_SUCCESS;
420 }
421
422 _PRCPU *_pr_current_cpu_tls()
423 {
424     _PRCPU *ret;
425
426     thr_getspecific(cpuid_key, (void **)&ret);
427     return ret;
428 }
429
430 PRThread *_pr_last_thread_tls()
431 {
432     PRThread *ret;
433
434     thr_getspecific(last_thread_key, (void **)&ret);
435     return ret;
436 }
437
438 _MDLock _pr_ioq_lock;
439
440 void _MD_INIT_IO (void)
441 {
442     _MD_NEW_LOCK(&_pr_ioq_lock);
443 }
444
445 PRStatus _MD_InitializeThread(PRThread *thread)
446 {
447     if (!_PR_IS_NATIVE_THREAD(thread))
448         return;
449                 /* prime the sp; substract 4 so we don't hit the assert that
450                  * curr sp > base_stack
451                  */
452     thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long);
453     thread->md.lwpid = _lwp_self();
454     thread->md.handle = THR_SELF();
455
456         /* all threads on Solaris are global threads from NSPR's perspective
457          * since all of them are mapped to Solaris threads.
458          */
459     thread->flags |= _PR_GLOBAL_SCOPE;
460
461         /* For primordial/attached thread, we don't create an underlying native thread.
462          * So, _MD_CREATE_THREAD() does not get called.  We need to do initialization
463          * like allocating thread's synchronization variables and set the underlying
464          * native thread's priority.
465          */
466         if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) {
467             _MD_NEW_SEM(&thread->md.waiter_sem, 0);
468             _MD_SET_PRIORITY(&(thread->md), thread->priority);
469         }
470         return PR_SUCCESS;
471 }
472
473 static sigset_t old_mask;       /* store away original gc thread sigmask */
474 static int gcprio;              /* store away original gc thread priority */
475 static lwpid_t *all_lwps=NULL;  /* list of lwps that we suspended */
476 static int num_lwps ;
477 static int suspendAllOn = 0;
478
479 #define VALID_SP(sp, bottom, top)       \
480        (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top)))
481
482 void unixware_preempt_off()
483 {
484     sigset_t set;
485     (void)sigfillset(&set);
486     sigprocmask (SIG_SETMASK, &set, &old_mask);
487 }
488
489 void unixware_preempt_on()
490 {
491     sigprocmask (SIG_SETMASK, &old_mask, NULL);      
492 }
493
494 void _MD_Begin_SuspendAll()
495 {
496     unixware_preempt_off();
497
498     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n"));
499     /* run at highest prio so I cannot be preempted */
500     thr_getprio(thr_self(), &gcprio);
501     thr_setprio(thr_self(), 0x7fffffff); 
502     suspendAllOn = 1;
503 }
504
505 void _MD_End_SuspendAll()
506 {
507 }
508
509 void _MD_End_ResumeAll()
510 {
511     PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n"));
512     thr_setprio(thr_self(), gcprio);
513     unixware_preempt_on();
514     suspendAllOn = 0;
515 }
516
517 void _MD_Suspend(PRThread *thr)
518 {
519    int lwp_fd, result;
520    int lwp_main_proc_fd = 0;
521
522     thr_suspend(thr->md.handle);
523     if (!_PR_IS_GCABLE_THREAD(thr))
524       return;
525     /* XXX Primordial thread can't be bound to an lwp, hence there is no
526      * way we can assume that we can get the lwp status for primordial
527      * thread reliably. Hence we skip this for primordial thread, hoping
528      * that the SP is saved during lock and cond. wait. 
529      * XXX - Again this is concern only for java interpreter, not for the
530      * server, 'cause primordial thread in the server does not do java work
531      */
532     if (thr->flags & _PR_PRIMORDIAL)
533       return;
534
535     /* if the thread is not started yet then don't do anything */
536     if (!suspendAllOn || thr->md.lwpid == -1)
537       return;
538
539 }
540 void _MD_Resume(PRThread *thr)
541 {
542    if (!_PR_IS_GCABLE_THREAD(thr) || !suspendAllOn){
543      /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend
544       * during that time we can't call any thread lib or libc calls. Hence
545       * make sure that no resume is requested for Non gcable thread
546       * during suspendAllOn */
547       PR_ASSERT(!suspendAllOn);
548       thr_continue(thr->md.handle);
549       return;
550    }
551    if (thr->md.lwpid == -1)
552      return;
553  
554    if ( _lwp_continue(thr->md.lwpid) < 0) {
555       PR_ASSERT(0);  /* ARGH, we are hosed! */
556    }
557 }
558
559
560 PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
561 {
562     if (isCurrent) {
563         (void) getcontext(CONTEXT(t));  /* XXX tune me: set md_IRIX.c */
564     }
565     *np = NGREG;
566     if (t->md.lwpid == -1)
567       memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord));
568     return (PRWord*) &t->md.context.uc_mcontext.gregs[0];
569 }
570
571 int
572 _pr_unixware_clock_gettime (struct timespec *tp)
573 {
574     struct timeval tv;
575  
576     gettimeofday(&tv, NULL);
577     tp->tv_sec = tv.tv_sec;
578     tp->tv_nsec = tv.tv_usec * 1000;
579     return 0;
580 }
581
582
583 #endif /* USE_SVR4_THREADS */