1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 * Copyright (c) 2008 Likewise Software, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1.  Redistributions of source code must retain the above copyright
12 *     notice, this list of conditions and the following disclaimer.
13 * 2.  Redistributions in binary form must reproduce the above copyright
14 *     notice, this list of conditions and the following disclaimer in the
15 *     documentation and/or other materials provided with the distribution.
16 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
17 *     contributors may be used to endorse or promote products derived from
18 *     this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Portions of this software have been released under the following terms:
32 *
33 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
34 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
35 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
36 *
37 * To anyone who acknowledges that this file is provided "AS IS"
38 * without any express or implied warranty:
39 * permission to use, copy, modify, and distribute this file for any
40 * purpose is hereby granted without fee, provided that the above
41 * copyright notices and this notice appears in all source code copies,
42 * and that none of the names of Open Software Foundation, Inc., Hewlett-
43 * Packard Company or Digital Equipment Corporation be used
44 * in advertising or publicity pertaining to distribution of the software
45 * without specific, written prior permission.  Neither Open Software
46 * Foundation, Inc., Hewlett-Packard Company nor Digital
47 * Equipment Corporation makes any representations about the suitability
48 * of this software for any purpose.
49 *
50 * Copyright (c) 2007, Novell, Inc. All rights reserved.
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
53 * are met:
54 *
55 * 1.  Redistributions of source code must retain the above copyright
56 *     notice, this list of conditions and the following disclaimer.
57 * 2.  Redistributions in binary form must reproduce the above copyright
58 *     notice, this list of conditions and the following disclaimer in the
59 *     documentation and/or other materials provided with the distribution.
60 * 3.  Neither the name of Novell Inc. nor the names of its contributors
61 *     may be used to endorse or promote products derived from this
62 *     this software without specific prior written permission.
63 *
64 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
65 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
66 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
67 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
68 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
69 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
70 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
71 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
72 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
73 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
74 *
75 * @APPLE_LICENSE_HEADER_END@
76 */
77
78#include "dcethread-private.h"
79#include "dcethread-debug.h"
80#include "dcethread-exception.h"
81
82#include <config.h>
83#include <stdlib.h>
84#include <signal.h>
85#include <string.h>
86#include <time.h>
87#include <sys/time.h>
88
89static pthread_once_t dcethread_init_once = DCETHREAD_ONCE_INIT;
90static pthread_key_t dcethread_self_key;
91static pthread_attr_t dcethread_attr_default;
92static pthread_mutexattr_t dcethread_mutexattr_default;
93static pthread_condattr_t dcethread_condattr_default;
94
95#ifdef SIGRTMIN
96#    define INTERRUPT_SIGNAL (SIGRTMIN + 5)
97#else
98#    define INTERRUPT_SIGNAL (SIGXCPU)
99#endif
100
101static void
102interrupt_signal_handler(int sig ATTRIBUTE_UNUSED)
103{
104    dcethread* thread = dcethread__self();
105
106    /* In asynchronous interrupt mode all bets
107       are off anyway, so don't bother to lock */
108    if (thread->flag.async)
109    {
110	dcethread__dispatchinterrupt(thread);
111    }
112}
113
114static void
115self_destructor(void* data)
116{
117    if (data)
118    {
119        dcethread* self = (dcethread*) data;
120        dcethread__lock(self);
121        dcethread__change_state(self, DCETHREAD_STATE_DEAD);
122        dcethread__release(self);
123        dcethread__unlock(self);
124    }
125}
126
127static void
128init(void)
129{
130    int cancelstate, oldstate;
131    struct sigaction act;
132
133    pthread_key_create(&dcethread_self_key, self_destructor);
134
135    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate);
136    pthread_attr_init(&dcethread_attr_default);
137    pthread_mutexattr_init(&dcethread_mutexattr_default);
138
139#if defined(RPC_MUTEX_DEBUG) && defined(__USE_UNIX98)
140    pthread_mutexattr_settype(&dcethread_mutexattr_default, PTHREAD_MUTEX_ERRORCHECK);
141#endif
142    pthread_condattr_init(&dcethread_condattr_default);
143    dcethread__init_exceptions();
144    pthread_setcancelstate(cancelstate, &oldstate);
145
146    sigemptyset(&act.sa_mask);
147    act.sa_handler = interrupt_signal_handler;
148    act.sa_flags = 0;
149    sigaction(INTERRUPT_SIGNAL, &act, NULL);
150    if (getenv("DCETHREAD_DEBUG"))
151	dcethread__debug_set_callback(dcethread__default_log_callback, NULL);
152}
153
154#ifdef __SUNPRO_C
155#pragma init dcethread__init
156#else
157void dcethread__init(void);
158#endif
159
160void dcethread__init(void)
161{
162    pthread_once(&dcethread_init_once, init);
163}
164
165int
166dcethread__interrupt_syscall(dcethread* thread, void* data ATTRIBUTE_UNUSED)
167{
168    pthread_kill(thread->pthread, INTERRUPT_SIGNAL);
169    return 0;
170}
171
172static int
173my_clock_gettime(struct timespec* tp)
174{
175#ifdef CLOCK_REALTIME
176  return clock_gettime(CLOCK_REALTIME, tp);
177#else
178  int result;
179  struct timeval tv;
180
181  if ((result = gettimeofday(&tv, NULL)))
182    return result;
183
184  tp->tv_sec = tv.tv_sec;
185  tp->tv_nsec = tv.tv_usec * 1000;
186
187  return 0;
188#endif
189}
190
191int
192dcethread__interrupt_condwait(dcethread* thread, void* data)
193{
194    condwait_info* info = (condwait_info*) data;
195
196    if (pthread_equal(info->mutex->owner, pthread_self()))
197    {
198        DCETHREAD_TRACE("Thread %p: already owned mutex used for interrupt", thread);
199        if (pthread_cond_broadcast(info->cond))
200        {
201            DCETHREAD_ERROR("Thread %p: broadcast failed", thread);
202            return 0;
203        }
204        else
205        {
206            DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread);
207            return 1;
208        }
209    }
210    else if (!pthread_mutex_trylock((pthread_mutex_t*) &info->mutex->mutex))
211    {
212        info->mutex->owner = pthread_self();
213        if (pthread_cond_broadcast(info->cond))
214        {
215            DCETHREAD_ERROR("Thread %p: broadcast failed", thread);
216            info->mutex->owner = (pthread_t) -1;
217            pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex);
218            return 0;
219        }
220        else
221        {
222            DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread);
223            info->mutex->owner = (pthread_t) -1;
224            pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex);
225            return 1;
226        }
227    }
228    else
229    {
230        DCETHREAD_VERBOSE("Thread %p: could not acquire lock to interrupt condwait", thread);
231        return 0;
232    }
233}
234
235dcethread*
236dcethread__new (void)
237{
238    dcethread* thread;
239
240    /* Ensure thread system is initialized */
241    dcethread__init();
242
243    thread = calloc(1, sizeof(dcethread));
244
245    thread->refs = 1;
246    thread->flag.interruptible = 1;
247    thread->flag.joinable = 0;
248    thread->flag.async = 0;
249    thread->state = DCETHREAD_STATE_CREATED;
250
251    pthread_mutex_init((pthread_mutex_t*) &thread->lock, NULL);
252    pthread_cond_init((pthread_cond_t*) &thread->state_change, NULL);
253
254    /* Set default interrupt handler that throws an interrupt exception */
255    thread->handle_interrupt = dcethread__exc_handle_interrupt;
256    thread->handle_interrupt_data = (void*) &dcethread_interrupt_e;
257
258    /* Set default interrupt method that pokes the thread with a signal */
259    thread->interrupt = dcethread__interrupt_syscall;
260    thread->interrupt_data = NULL;
261
262    return thread;
263}
264
265dcethread*
266dcethread__self(void)
267{
268    dcethread* thread;
269
270    /* Ensure thread system is initialized */
271    dcethread__init();
272
273    /* Get self pointer from TLS */
274    thread = (dcethread*) pthread_getspecific(dcethread_self_key);
275    if (!thread)
276    {
277	/* Lazily create it if it didn't already exist */
278	thread = dcethread__new();
279	thread->pthread = pthread_self();
280	pthread_setspecific(dcethread_self_key, (void*) thread);
281	thread->state = DCETHREAD_STATE_ACTIVE;
282    }
283
284    return thread;
285}
286
287void
288dcethread__init_self(dcethread* thread)
289{
290    /* Ensure thread system is initialized */
291    dcethread__init();
292
293    pthread_setspecific(dcethread_self_key, (void*) thread);
294    dcethread__lock(thread);
295    dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE);
296    dcethread__unlock(thread);
297}
298
299static void
300dcethread__sanity(dcethread* thread)
301{
302    if (!thread) {
303        DCETHREAD_ERROR("NULL thread encountered");
304        return;
305    }
306    if ((int)thread->refs < 0)
307        DCETHREAD_ERROR("Thread %p: ref count < 0", thread);
308    if (!thread->flag.locked)
309        DCETHREAD_ERROR("Thread %p: not locked when expected", thread);
310    switch (thread->state)
311    {
312    case DCETHREAD_STATE_CREATED:
313    case DCETHREAD_STATE_ACTIVE:
314    case DCETHREAD_STATE_BLOCKED:
315    case DCETHREAD_STATE_INTERRUPT:
316        if (thread->refs == 0)
317            DCETHREAD_ERROR("Thread %p: ref count = 0 in living thread",
318		    dcethread__self());
319        break;
320    case DCETHREAD_STATE_DEAD:
321        break;
322    }
323}
324
325void
326dcethread__delete(dcethread* thread)
327{
328    DCETHREAD_TRACE("Thread %p: deleted", thread);
329    pthread_mutex_destroy((pthread_mutex_t*) &thread->lock);
330    pthread_cond_destroy((pthread_cond_t*) &thread->state_change);
331    if (thread->flag.joinable)
332        pthread_detach(thread->pthread);
333    free((void*) thread);
334}
335
336void
337dcethread__retain(dcethread* thread)
338{
339    dcethread__sanity(thread);
340    if (thread->refs == 0)
341    {
342	DCETHREAD_ERROR("Attempted to retain freed thread %p", thread);
343    }
344    else
345    {
346	thread->refs++;
347	DCETHREAD_TRACE("Thread %p: ref count increased to %i", thread, thread->refs);
348    }
349}
350
351void
352dcethread__release(dcethread* thread)
353{
354    dcethread__sanity(thread);
355    if (thread->refs <= 0)
356    {
357	DCETHREAD_ERROR("Thread %p: attempted to release freed thread", thread);
358    }
359    else
360    {
361	thread->refs--;
362	DCETHREAD_TRACE("Thread %p: ref count decreased to %i", thread, thread->refs);
363    }
364}
365
366void
367dcethread__lock(dcethread* thread)
368{
369    if (pthread_mutex_lock((pthread_mutex_t*) &thread->lock))
370        DCETHREAD_ERROR("Thread %p: failed to lock mutex", thread);
371    thread->flag.locked = 1;
372    dcethread__sanity(thread);
373    DCETHREAD_TRACE("Thread %p: locked", thread);
374}
375
376void
377dcethread__unlock(dcethread* thread)
378{
379    unsigned int refs;
380
381    dcethread__sanity(thread);
382
383    /* Access reference count while thread is still locked
384       in order to avoid race conditions */
385    refs = thread->refs;
386
387    thread->flag.locked = 0;
388    if (pthread_mutex_unlock((pthread_mutex_t*) &thread->lock))
389        DCETHREAD_ERROR("Thread %p: failed to unlock mutex", thread);
390    DCETHREAD_TRACE("Thread %p: unlocked", thread);
391
392    if (refs == 0)
393    {
394	dcethread__delete(thread);
395    }
396}
397
398void
399dcethread__wait(dcethread* thread)
400{
401    dcethread__sanity(thread);
402    thread->flag.locked = 0;
403    pthread_cond_wait((pthread_cond_t*) &thread->state_change,
404                      (pthread_mutex_t*) &thread->lock);
405    thread->flag.locked = 1;
406}
407
408void
409dcethread__timedwait(dcethread* thread, struct timespec* ts)
410{
411    dcethread__sanity(thread);
412    thread->flag.locked = 0;
413    pthread_cond_timedwait((pthread_cond_t*) &thread->state_change,
414                           (pthread_mutex_t*) &thread->lock, ts);
415    thread->flag.locked = 1;
416}
417
418static const char*
419state_name(int state)
420{
421#define CASE(token) case token: return #token
422    switch (state)
423    {
424	CASE(DCETHREAD_STATE_CREATED);
425	CASE(DCETHREAD_STATE_ACTIVE);
426	CASE(DCETHREAD_STATE_BLOCKED);
427	CASE(DCETHREAD_STATE_INTERRUPT);
428	CASE(DCETHREAD_STATE_DEAD);
429    }
430    return "UNKNOWN";
431#undef CASE
432}
433
434void
435dcethread__change_state(dcethread* thread, int state)
436{
437    DCETHREAD_TRACE("Thread %p: state transition %s -> %s",
438		thread,
439		state_name(thread->state),
440		state_name(state));
441    thread->state = state;
442    pthread_cond_broadcast((pthread_cond_t*) &thread->state_change);
443}
444
445#ifndef HAVE_PTHREAD_LOCK_GLOBAL_NP
446static pthread_mutex_t dcethread_g_global_lock = PTHREAD_MUTEX_INITIALIZER;
447#endif /* HAVE_PTHREAD_LOCK_GLOBAL_NP */
448
449void dcethread__lock_global(void)
450{
451#ifdef HAVE_PTHREAD_LOCK_GLOBAL_NP
452    pthread_lock_global_np();
453#else
454    pthread_mutex_lock(&dcethread_g_global_lock);
455#endif /* HAVE_PTHREAD_LOCK_GLOBAL_NP */
456}
457
458void dcethread__unlock_global(void)
459{
460#ifdef HAVE_PTHREAD_UNLOCK_GLOBAL_NP
461    pthread_unlock_global_np();
462#else
463    pthread_mutex_unlock(&dcethread_g_global_lock);
464#endif /* HAVE_PTHREAD_UNLOCK_GLOBAL_NP */
465}
466
467void
468dcethread__dispatchinterrupt(dcethread* thread)
469{
470    DCETHREAD_TRACE("Thread %p: interrupt acknowledged", thread);
471    thread->handle_interrupt(thread, thread->handle_interrupt_data);
472}
473
474void
475dcethread__interrupt(dcethread* thread)
476{
477    int count = 0;
478    int old_state = thread->state;
479
480    if (old_state == DCETHREAD_STATE_INTERRUPT ||
481        old_state == DCETHREAD_STATE_DEAD)
482    {
483        /* Don't bother */
484        return;
485    }
486
487    DCETHREAD_TRACE("Thread %p: interrupt posted", thread);
488    dcethread__change_state(thread, DCETHREAD_STATE_INTERRUPT);
489
490    /* We need to poke the thread and wait for an acknowledgement of the interrupt if: */
491    if (thread != dcethread__self() &&         /* The interrupted thread is not us, and */
492        thread->flag.interruptible &&          /* The thread can be interrupted, and */
493        old_state == DCETHREAD_STATE_BLOCKED)  /* The thread was blocked */
494    {
495        /* FIXME: potential livelock here if another thread
496           re-interrupts when the lock is released */
497        while (thread->state == DCETHREAD_STATE_INTERRUPT)
498        {
499            struct timespec waittime;
500
501            if (count > 2)
502                DCETHREAD_WARNING("Thread %p: still not interrupted after %i ms", thread, count * 100);
503
504            if (thread->interrupt(thread, thread->interrupt_data))
505            {
506                /* Interrupt is guaranteed to have succeeded, so
507                   leave state change wait loop */
508                break;
509            }
510
511            count++;
512
513            my_clock_gettime(&waittime);
514            waittime.tv_nsec += 100000000;
515
516            if (waittime.tv_nsec > 1000000000)
517            {
518	       waittime.tv_nsec -= 1000000000;
519	       waittime.tv_sec += 1;
520	    }
521
522            /* Wait for state change */
523            dcethread__timedwait(thread, &waittime);
524        }
525    }
526}
527
528void
529dcethread__set_interrupt_handler(dcethread* thread, void (*handle_interrupt)(dcethread*, void*), void* data)
530{
531    thread->handle_interrupt = handle_interrupt;
532    thread->handle_interrupt_data = data;
533}
534
535int
536dcethread__begin_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data,
537                       int (**old_interrupt)(dcethread*, void*), void** old_data)
538{
539    int state;
540    int interruptible;
541
542    dcethread__lock(thread);
543    state = thread->state;
544    interruptible = thread->flag.interruptible;
545    /* If thread is currently active */
546    if (state == DCETHREAD_STATE_ACTIVE)
547    {
548	/* Set up interruption callbacks */
549	if (old_interrupt)
550	    *old_interrupt = thread->interrupt;
551	if (old_data)
552	    *old_data = thread->interrupt_data;
553	if (interrupt)
554	    thread->interrupt = interrupt;
555	if (data)
556	    thread->interrupt_data = data;
557
558	/* Change to blocked state */
559	dcethread__change_state(thread, DCETHREAD_STATE_BLOCKED);
560    }
561    /* If an interrupt request has been posted (and we can be interrupted) */
562    else if (state == DCETHREAD_STATE_INTERRUPT && interruptible)
563    {
564	/* Clear request */
565	dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE);
566    }
567    dcethread__unlock(thread);
568
569    return state == DCETHREAD_STATE_INTERRUPT && interruptible;
570}
571
572int
573dcethread__poll_end_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data)
574{
575    int state;
576    int interruptible;
577
578    dcethread__lock(thread);
579    state = thread->state;
580    interruptible = thread->flag.interruptible;
581
582    if (state == DCETHREAD_STATE_INTERRUPT)
583    {
584        if (interrupt)
585            thread->interrupt = interrupt;
586        if (data)
587            thread->interrupt_data = data;
588        if ((interruptible && state == DCETHREAD_STATE_INTERRUPT) ||
589            (state == DCETHREAD_STATE_BLOCKED))
590            dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE);
591    }
592
593    dcethread__unlock(thread);
594
595    return state == DCETHREAD_STATE_INTERRUPT && interruptible;
596}
597
598int
599dcethread__end_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data)
600{
601    int state;
602    int interruptible;
603
604    dcethread__lock(thread);
605    state = thread->state;
606    interruptible = thread->flag.interruptible;
607
608    /* Switch state back to active if:
609       - We were not interrupted during the block
610       - We were interrupted and the thread is interruptible
611
612       If we were interrupted but are not currently interruptible,
613       we want to leave the state alone so that is is caught when
614       interruptiblility is enabled again.
615    */
616    if ((interruptible && state == DCETHREAD_STATE_INTERRUPT) ||
617        (state == DCETHREAD_STATE_BLOCKED))
618    {
619        if (interrupt)
620            thread->interrupt = interrupt;
621        if (data)
622            thread->interrupt_data = data;
623        dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE);
624    }
625
626    dcethread__unlock(thread);
627
628    return state == DCETHREAD_STATE_INTERRUPT && interruptible;
629}
630
631void
632dcethread__cleanup_self(dcethread* self)
633{
634    /* Ensure thread system is initialized */
635    dcethread__init();
636
637    dcethread__change_state(self, DCETHREAD_STATE_DEAD);
638    dcethread__release(self);
639    pthread_setspecific(dcethread_self_key, NULL);
640}
641