1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      rpcmutex.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  The support routines for rpcmutex.h abstraction.  These should NOT
90**  be called directly; use the macros in rpcmutex.h .
91**
92**
93*/
94
95#include <commonp.h>
96
97#if defined(RPC_MUTEX_DEBUG) || defined(RPC_MUTEX_STATS)
98
99/*
100 * All of the routines return true on success, false on failure.
101 *
102 * There are races present on the modifications of the package wide
103 * statistics as well as the per lock "busy", "try_lock" and "*_assert"
104 * statistics, but we don't really care.  We don't want to burden these
105 * "informative" statistics with some other mutex.  We're only providing
106 * these stats so we can track gross trends.
107 */
108
109/*
110 * !!! Since CMA pthreads doesn't provide a "null" handle, create our own.
111 */
112PRIVATE dcethread* rpc_g_null_thread_handle;
113#define NULL_THREAD rpc_g_null_thread_handle
114
115#define IS_MY_THREAD(t) dcethread_equal((t), my_thread)
116
117/*
118 * Some package wide statistics.
119 */
120
121INTERNAL rpc_mutex_stats_t mutex_stats = {0};
122INTERNAL rpc_cond_stats_t cond_stats = {0};
123
124/*
125 * R P C _ _ M U T E X _ I N I T
126 */
127
128PRIVATE boolean rpc__mutex_init
129(
130    rpc_mutex_p_t mp
131)
132{
133    mp->stats.busy = 0;
134    mp->stats.lock = 0;
135    mp->stats.try_lock = 0;
136    mp->stats.unlock = 0;
137    mp->stats.init = 1;
138    mp->stats.deletes = 0;
139    mp->stats.lock_assert = 0;
140    mp->stats.unlock_assert = 0;
141    mp->is_locked = false;
142    mp->owner = NULL_THREAD;
143    mp->locker_file = "never_locked";
144    mp->locker_line = 0;
145    if (dcethread_mutex_init(&mp->m, NULL) != 0) {
146	return (false);
147    }
148    mutex_stats.init++;
149    return(true);
150}
151
152/*
153 * R P C _ _ M U T E X _ D E L E T E
154 */
155
156PRIVATE boolean rpc__mutex_delete
157(
158    rpc_mutex_p_t mp
159)
160{
161    mp->stats.deletes++;
162    mutex_stats.deletes++;
163    dcethread_mutex_destroy(&mp->m);
164    return(true);
165}
166
167/*
168 * R P C _ _ M U T E X _ L O C K
169 */
170
171PRIVATE boolean rpc__mutex_lock
172(
173    rpc_mutex_p_t mp,
174    char *file,
175    int line
176)
177{
178    dcethread* my_thread = NULL_THREAD;
179    boolean is_locked = mp->is_locked;
180    boolean dbg;
181
182    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
183    if (dbg)
184    {
185        my_thread = dcethread_self();
186        if (is_locked && IS_MY_THREAD(mp->owner))
187        {
188            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
189		("(rpc__mutex_lock) deadlock with self at %s/%d (previous lock at %s/%d)\n",
190                file, line, mp->locker_file, mp->locker_line));
191            return(false);
192        }
193    }
194    dcethread_mutex_lock(&mp->m);
195    mp->is_locked = true;
196    if (dbg)
197    {
198        mp->owner = my_thread;
199        mp->locker_file = file;
200        mp->locker_line = line;
201    }
202    if (is_locked)
203    {
204        mp->stats.busy++;
205        mutex_stats.busy++;
206    }
207    mp->stats.lock++;
208    mutex_stats.lock++;
209    return(true);
210}
211
212/*
213 * R P C _ _ M U T E X _ T R Y _ L O C K
214 */
215
216PRIVATE boolean rpc__mutex_try_lock
217(
218    rpc_mutex_p_t mp,
219    boolean *bp,
220    char *file,
221    int line
222)
223{
224    dcethread* my_thread = NULL_THREAD;
225    boolean is_locked = mp->is_locked;
226    boolean dbg;
227
228    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
229    if (dbg)
230    {
231        my_thread = dcethread_self();
232        if (is_locked && IS_MY_THREAD(mp->owner))
233        {
234            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
235		("(rpc__mutex_try_lock) deadlock with self at %s/%d (previous lock at %s/%d)\n",
236                file, line, mp->locker_file, mp->locker_line));
237            return(false);
238        }
239    }
240    *bp = dcethread_mutex_trylock(&mp->m);
241    if (*bp)
242    {
243        mp->is_locked = true;
244        if (dbg)
245        {
246            mp->owner = my_thread;
247            mp->locker_file = file;
248            mp->locker_line = line;
249        }
250    }
251    else
252    {
253        mp->stats.busy++;
254        mutex_stats.busy++;
255    }
256    mp->stats.try_lock++;
257    mutex_stats.try_lock++;
258    return(true);
259}
260
261/*
262 * R P C _ _ M U T E X _ U N L O C K
263 */
264
265PRIVATE boolean rpc__mutex_unlock
266(
267    rpc_mutex_p_t mp
268)
269{
270    dcethread* my_thread;
271    boolean is_locked = mp->is_locked;
272    boolean dbg;
273
274    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
275    if (dbg)
276    {
277        if (! is_locked)
278        {
279            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
280		("(rpc__mutex_unlock) not locked\n"));
281            return(false);
282        }
283        my_thread = dcethread_self();
284        if (!IS_MY_THREAD(mp->owner))
285        {
286            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
287		("(rpc__mutex_unlock) not owner (owner at %s/%d)\n",
288                mp->locker_file, mp->locker_line));
289            return(false);
290        }
291        mp->owner = NULL_THREAD;
292    }
293    mp->stats.unlock++;
294    mutex_stats.unlock++;
295    mp->is_locked = false;
296    dcethread_mutex_unlock(&mp->m);
297    return(true);
298}
299
300/*
301 * R P C _ _ M U T E X _ L O C K _ A S S E R T
302 *
303 * assert that we are the owner of the lock.
304 */
305
306PRIVATE boolean rpc__mutex_lock_assert
307(
308    rpc_mutex_p_t mp
309)
310{
311    dcethread* my_thread;
312    boolean is_locked = mp->is_locked;
313    boolean dbg;
314
315    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
316    mp->stats.lock_assert++;
317    mutex_stats.lock_assert++;
318    if (dbg)
319    {
320        if (! is_locked)
321        {
322            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
323		("(rpc__mutex_lock_assert) not locked\n"));
324            return(false);
325        }
326        my_thread = dcethread_self();
327        if (!IS_MY_THREAD(mp->owner))
328        {
329            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
330	       ("(rpc__mutex_lock_assert) not owner\n"));
331            return(false);
332        }
333    }
334    return(true);
335}
336
337/*
338 * R P C _ _ M U T E X _ U N L O C K _ A S S E R T
339 *
340 * assert that we are not the owner of the lock.
341 */
342
343PRIVATE boolean rpc__mutex_unlock_assert
344(
345    rpc_mutex_p_t mp
346)
347{
348    dcethread* my_thread;
349    boolean is_locked = mp->is_locked;
350    boolean dbg;
351
352    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
353    mp->stats.unlock_assert++;
354    mutex_stats.unlock_assert++;
355    if (dbg)
356    {
357        if (! is_locked)
358            return(true);
359        my_thread = dcethread_self();
360        if (IS_MY_THREAD(mp->owner))
361        {
362            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
363		("(rpc__mutex_unlock_assert) owner\n"));
364            return(false);
365        }
366    }
367    return(true);
368}
369
370/*
371 * R P C _ _ C O N D _ I N I T
372 *
373 * The "mp" is the mutex that is associated with the cv.
374 */
375
376boolean rpc__cond_init
377(
378    rpc_cond_p_t cp,
379    rpc_mutex_p_t mp
380)
381{
382    cp->stats.init = 1;
383    cp->stats.deletes = 0;
384    cp->stats.wait = 0;
385    cp->stats.signals = 0;
386    cp->mp = mp;
387    dcethread_cond_init(&cp->c, NULL);
388    cond_stats.init++;
389    return(true);
390}
391
392/*
393 * R P C _ _ C O N D _ D E L E T E
394 */
395
396boolean rpc__cond_delete
397(
398    rpc_cond_p_t cp,
399    rpc_mutex_p_t mp ATTRIBUTE_UNUSED
400)
401{
402    cp->stats.deletes++;
403    cond_stats.deletes++;
404    dcethread_cond_destroy(&cp->c);
405    return(true);
406}
407
408/*
409 *  R P C _ _ C O N D _ W A I T
410 *
411 * The mutex is automatically released and reacquired by the wait.
412 */
413
414boolean rpc__cond_wait
415(
416    rpc_cond_p_t cp,
417    rpc_mutex_p_t mp,
418    char *file,
419    int line
420)
421{
422    dcethread* my_thread;
423    volatile boolean dbg;
424
425    DO_NOT_CLOBBER(my_thread);
426
427    cp->stats.wait++;
428    cond_stats.wait++;
429    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
430    if (dbg)
431    {
432        if (! rpc__mutex_lock_assert(mp))
433        {
434            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
435		("(rpc__cond_wait) mutex usage error\n"));
436            return(false);
437        }
438        if (cp->mp != mp)
439        {
440            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
441		("(rpc__cond_wait) incorrect mutex\n"));
442            return(false);
443        }
444        my_thread = dcethread_self();
445        mp->owner = NULL_THREAD;
446    }
447    mp->is_locked = false;
448    TRY
449        dcethread_cond_wait_throw(&cp->c, &mp->m);
450    CATCH_ALL
451        mp->is_locked = true;
452        if (dbg)
453        {
454            mp->owner = my_thread;
455            mp->locker_file = file;
456            mp->locker_line = line;
457        }
458        RERAISE;
459    ENDTRY
460    mp->is_locked = true;
461    if (dbg)
462    {
463        mp->owner = my_thread;
464        mp->locker_file = file;
465        mp->locker_line = line;
466    }
467    return(true);
468}
469
470/*
471 *  R P C _ _ C O N D _ T I M E D _ W A I T
472 *
473 * The mutex is automatically released and reacquired by the wait.
474 */
475
476boolean rpc__cond_timed_wait
477(
478    rpc_cond_p_t cp,
479    rpc_mutex_p_t mp,
480    struct timespec *wtime,
481    char *file,
482    int line
483)
484{
485    dcethread* my_thread;
486    volatile boolean dbg;
487
488    DO_NOT_CLOBBER(my_thread);
489
490    cp->stats.wait++;
491    cond_stats.wait++;
492    dbg = RPC_DBG(rpc_es_dbg_mutex, 5);
493    if (dbg)
494    {
495        if (! rpc__mutex_lock_assert(mp))
496        {
497            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
498		("(rpc__cond_wait) mutex usage error\n"));
499            return(false);
500        }
501        if (cp->mp != mp)
502        {
503            RPC_DBG_PRINTF(rpc_e_dbg_mutex, 1,
504		("(rpc__cond_wait) incorrect mutex\n"));
505            return(false);
506        }
507        my_thread = dcethread_self();
508        mp->owner = NULL_THREAD;
509    }
510    mp->is_locked = false;
511    TRY	{
512        dcethread_cond_timedwait_throw(&cp->c, &mp->m, wtime);
513	}
514    CATCH_ALL
515        mp->is_locked = true;
516        if (dbg)
517        {
518            mp->owner = my_thread;
519            mp->locker_file = file;
520            mp->locker_line = line;
521        }
522        RERAISE;
523    ENDTRY
524    mp->is_locked = true;
525    if (dbg)
526    {
527        mp->owner = my_thread;
528        mp->locker_file = file;
529        mp->locker_line = line;
530    }
531    return(true);
532}
533
534/*
535 *  R P C _ _ C O N D _ S I G N A L
536 *
537 * It's not clear if it's legal to signal w/o holding the lock
538 * (in the runtime's context);  CMA clearly doesn't require it.
539 */
540
541boolean rpc__cond_signal
542(
543    rpc_cond_p_t cp,
544    rpc_mutex_p_t mp ATTRIBUTE_UNUSED
545)
546{
547    cp->stats.signals++;
548    cond_stats.signals++;
549    dcethread_cond_signal(&cp->c);
550    return(true);
551}
552
553/*
554 *  R P C _ _ C O N D _ B R O A D C A S T
555 *
556 * It's not clear if it's legal to broadcast w/o holding the lock
557 * (in the runtime's context);  CMA clearly doesn't require it.
558 */
559
560boolean rpc__cond_broadcast
561(
562    rpc_cond_p_t cp,
563    rpc_mutex_p_t mp ATTRIBUTE_UNUSED
564)
565{
566    cp->stats.signals++;
567    cond_stats.signals++;
568    dcethread_cond_broadcast(&cp->c);
569    return(true);
570}
571
572#else
573#ifdef MIREK_NOT_DEFINED
574INTERNAL void rpc__mutex_none (void)
575{
576}
577#endif
578#endif /* defined(RPC_MUTEX_DEBUG) || defined(RPC_MUTEX_STATS) */
579