~ubuntu-branches/ubuntu/trusty/xulrunner/trusty

« back to all changes in this revision

Viewing changes to security/nss-fips/lib/util/nssrwlk.c

  • Committer: Bazaar Package Importer
  • Author(s): Devid Antonio Filoni
  • Date: 2008-08-25 13:04:18 UTC
  • mfrom: (1.1.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20080825130418-ck1i2ms384tzb9m0
Tags: 1.8.1.16+nobinonly-0ubuntu1
* New upstream release (taken from upstream CVS), LP: #254618.
* Fix MFSA 2008-35, MFSA 2008-34, MFSA 2008-33, MFSA 2008-32, MFSA 2008-31,
  MFSA 2008-30, MFSA 2008-29, MFSA 2008-28, MFSA 2008-27, MFSA 2008-25,
  MFSA 2008-24, MFSA 2008-23, MFSA 2008-22, MFSA 2008-21, MFSA 2008-26 also
  known as CVE-2008-2933, CVE-2008-2785, CVE-2008-2811, CVE-2008-2810,
  CVE-2008-2809, CVE-2008-2808, CVE-2008-2807, CVE-2008-2806, CVE-2008-2805,
  CVE-2008-2803, CVE-2008-2802, CVE-2008-2801, CVE-2008-2800, CVE-2008-2798.
* Drop 89_bz419350_attachment_306066 patch, merged upstream.
* Bump Standards-Version to 3.8.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ***** BEGIN LICENSE BLOCK *****
 
2
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
3
 *
 
4
 * The contents of this file are subject to the Mozilla Public License Version
 
5
 * 1.1 (the "License"); you may not use this file except in compliance with
 
6
 * the License. You may obtain a copy of the License at
 
7
 * http://www.mozilla.org/MPL/
 
8
 *
 
9
 * Software distributed under the License is distributed on an "AS IS" basis,
 
10
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
11
 * for the specific language governing rights and limitations under the
 
12
 * License.
 
13
 *
 
14
 * The Original Code is the Netscape security libraries.
 
15
 *
 
16
 * The Initial Developer of the Original Code is
 
17
 * Netscape Communications Corporation.
 
18
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 
19
 * the Initial Developer. All Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 *
 
23
 * Alternatively, the contents of this file may be used under the terms of
 
24
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
25
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
26
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
27
 * of those above. If you wish to allow use of your version of this file only
 
28
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
29
 * use your version of this file under the terms of the MPL, indicate your
 
30
 * decision by deleting the provisions above and replace them with the notice
 
31
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
32
 * the provisions above, a recipient may use your version of this file under
 
33
 * the terms of any one of the MPL, the GPL or the LGPL.
 
34
 *
 
35
 * ***** END LICENSE BLOCK ***** */
 
36
 
 
37
#include "nssrwlk.h"
 
38
#include "nspr.h"
 
39
 
 
40
PR_BEGIN_EXTERN_C
 
41
 
 
42
/*
 
43
 * Reader-writer lock
 
44
 */
 
45
struct nssRWLockStr {
 
46
    PZLock *        rw_lock;
 
47
    char   *        rw_name;            /* lock name                    */
 
48
    PRUint32        rw_rank;            /* rank of the lock             */
 
49
    PRInt32         rw_writer_locks;    /* ==  0, if unlocked           */
 
50
    PRInt32         rw_reader_locks;    /* ==  0, if unlocked           */
 
51
                                        /* > 0  , # of read locks       */
 
52
    PRUint32        rw_waiting_readers; /* number of waiting readers    */
 
53
    PRUint32        rw_waiting_writers; /* number of waiting writers    */
 
54
    PZCondVar *     rw_reader_waitq;    /* cvar for readers             */
 
55
    PZCondVar *     rw_writer_waitq;    /* cvar for writers             */
 
56
    PRThread  *     rw_owner;           /* lock owner for write-lock    */
 
57
                                        /* Non-null if write lock held. */
 
58
};
 
59
 
 
60
PR_END_EXTERN_C
 
61
 
 
62
#include <string.h>
 
63
 
 
64
#ifdef DEBUG_RANK_ORDER
 
65
#define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using
 
66
                                       rank-order for locks
 
67
                                    */
 
68
#endif
 
69
 
 
70
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
71
 
 
72
static PRUintn  nss_thread_rwlock_initialized;
 
73
static PRUintn  nss_thread_rwlock;               /* TPD key for lock stack */
 
74
static PRUintn  nss_thread_rwlock_alloc_failed;
 
75
 
 
76
#define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
 
77
 
 
78
typedef struct thread_rwlock_stack {
 
79
    PRInt32     trs_index;                                  /* top of stack */
 
80
    NSSRWLock    *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT];  /* stack of lock
 
81
                                                               pointers */
 
82
} thread_rwlock_stack;
 
83
 
 
84
/* forward static declarations. */
 
85
static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
 
86
static void     nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
 
87
static void     nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
 
88
static void     nssRWLock_ReleaseLockStack(void *lock_stack);
 
89
 
 
90
#endif
 
91
 
 
92
#define UNTIL(x) while(!(x))
 
93
 
 
94
/*
 
95
 * Reader/Writer Locks
 
96
 */
 
97
 
 
98
/*
 
99
 * NSSRWLock_New
 
100
 *      Create a reader-writer lock, with the given lock rank and lock name
 
101
 *
 
102
 */
 
103
 
 
104
PR_IMPLEMENT(NSSRWLock *)
 
105
NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
 
106
{
 
107
    NSSRWLock *rwlock;
 
108
 
 
109
    rwlock = PR_NEWZAP(NSSRWLock);
 
110
    if (rwlock == NULL)
 
111
        return NULL;
 
112
 
 
113
    rwlock->rw_lock = PZ_NewLock(nssILockRWLock);
 
114
    if (rwlock->rw_lock == NULL) {
 
115
        goto loser;
 
116
    }
 
117
    rwlock->rw_reader_waitq = PZ_NewCondVar(rwlock->rw_lock);
 
118
    if (rwlock->rw_reader_waitq == NULL) {
 
119
        goto loser;
 
120
    }
 
121
    rwlock->rw_writer_waitq = PZ_NewCondVar(rwlock->rw_lock);
 
122
    if (rwlock->rw_writer_waitq == NULL) {
 
123
        goto loser;
 
124
    }
 
125
    if (lock_name != NULL) {
 
126
        rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
 
127
        if (rwlock->rw_name == NULL) {
 
128
            goto loser;
 
129
        }
 
130
        strcpy(rwlock->rw_name, lock_name);
 
131
    } else {
 
132
        rwlock->rw_name = NULL;
 
133
    }
 
134
    rwlock->rw_rank            = lock_rank;
 
135
    rwlock->rw_waiting_readers = 0;
 
136
    rwlock->rw_waiting_writers = 0;
 
137
    rwlock->rw_reader_locks    = 0;
 
138
    rwlock->rw_writer_locks    = 0;
 
139
 
 
140
    return rwlock;
 
141
 
 
142
loser:
 
143
    NSSRWLock_Destroy(rwlock);
 
144
    return(NULL);
 
145
}
 
146
 
 
147
/*
 
148
** Destroy the given RWLock "lock".
 
149
*/
 
150
PR_IMPLEMENT(void)
 
151
NSSRWLock_Destroy(NSSRWLock *rwlock)
 
152
{
 
153
    PR_ASSERT(rwlock != NULL);
 
154
    PR_ASSERT(rwlock->rw_waiting_readers == 0);
 
155
 
 
156
    /* XXX Shouldn't we lock the PZLock before destroying this?? */
 
157
 
 
158
    if (rwlock->rw_name)
 
159
        PR_Free(rwlock->rw_name);
 
160
    if (rwlock->rw_reader_waitq)
 
161
        PZ_DestroyCondVar(rwlock->rw_reader_waitq);
 
162
    if (rwlock->rw_writer_waitq)
 
163
        PZ_DestroyCondVar(rwlock->rw_writer_waitq);
 
164
    if (rwlock->rw_lock)
 
165
        PZ_DestroyLock(rwlock->rw_lock);
 
166
    PR_DELETE(rwlock);
 
167
}
 
168
 
 
169
/***********************************************************************
 
170
**  Given the address of a NULL pointer to a NSSRWLock, 
 
171
**  atomically initializes that pointer to a newly created NSSRWLock.
 
172
**  Returns the value placed into that pointer, or NULL.
 
173
**   If the lock cannot be created because of resource constraints, 
 
174
**   the pointer will be left NULL.
 
175
**  
 
176
***********************************************************************/
 
177
PR_IMPLEMENT(NSSRWLock *)
 
178
nssRWLock_AtomicCreate( NSSRWLock  ** prwlock, 
 
179
                        PRUint32      lock_rank, 
 
180
                        const char *  lock_name)
 
181
{
 
182
    NSSRWLock  *    rwlock;
 
183
    static PRInt32  initializers;
 
184
 
 
185
    PR_ASSERT(prwlock != NULL);
 
186
 
 
187
    /* atomically initialize the lock */
 
188
    while (NULL == (rwlock = *prwlock)) {
 
189
        PRInt32 myAttempt = PR_AtomicIncrement(&initializers);
 
190
        if (myAttempt == 1) {
 
191
            if (NULL == (rwlock = *prwlock)) {
 
192
                *prwlock = rwlock = NSSRWLock_New(lock_rank, lock_name);
 
193
            }
 
194
            (void) PR_AtomicDecrement(&initializers);
 
195
            break;
 
196
        }
 
197
        PR_Sleep(PR_INTERVAL_NO_WAIT);          /* PR_Yield() */
 
198
        (void) PR_AtomicDecrement(&initializers);
 
199
    }
 
200
 
 
201
    return rwlock;
 
202
}
 
203
 
 
204
/*
 
205
** Read-lock the RWLock.
 
206
*/
 
207
PR_IMPLEMENT(void)
 
208
NSSRWLock_LockRead(NSSRWLock *rwlock)
 
209
{
 
210
    PRThread *me = PR_GetCurrentThread();
 
211
 
 
212
    PZ_Lock(rwlock->rw_lock);
 
213
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
214
 
 
215
    /*
 
216
     * assert that rank ordering is not violated; the rank of 'rwlock' should
 
217
     * be equal to or greater than the highest rank of all the locks held by
 
218
     * the thread.
 
219
     */
 
220
    PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
 
221
              (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
 
222
#endif
 
223
    /*
 
224
     * wait if write-locked or if a writer is waiting; preference for writers
 
225
     */
 
226
    UNTIL ( (rwlock->rw_owner == me) ||           /* I own it, or        */
 
227
           ((rwlock->rw_owner == NULL) &&         /* no-one owns it, and */
 
228
            (rwlock->rw_waiting_writers == 0))) { /* no-one is waiting to own */
 
229
 
 
230
        rwlock->rw_waiting_readers++;
 
231
        PZ_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
 
232
        rwlock->rw_waiting_readers--;
 
233
    }
 
234
    rwlock->rw_reader_locks++;          /* Increment read-lock count */
 
235
 
 
236
    PZ_Unlock(rwlock->rw_lock);
 
237
 
 
238
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
239
    nssRWLock_SetThreadRank(me, rwlock);/* update thread's lock rank */
 
240
#endif
 
241
}
 
242
 
 
243
/* Unlock a Read lock held on this RW lock.
 
244
*/
 
245
PR_IMPLEMENT(void)
 
246
NSSRWLock_UnlockRead(NSSRWLock *rwlock)
 
247
{
 
248
    PZ_Lock(rwlock->rw_lock);
 
249
 
 
250
    PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
 
251
 
 
252
    if ((  rwlock->rw_reader_locks  > 0)  &&    /* caller isn't screwey */
 
253
        (--rwlock->rw_reader_locks == 0)  &&    /* not read locked any more */
 
254
        (  rwlock->rw_owner        == NULL) &&  /* not write locked */
 
255
        (  rwlock->rw_waiting_writers > 0)) {   /* someone's waiting. */
 
256
 
 
257
        PZ_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
 
258
    }
 
259
 
 
260
    PZ_Unlock(rwlock->rw_lock);
 
261
 
 
262
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
263
    /*
 
264
     * update thread's lock rank
 
265
     */
 
266
    nssRWLock_UnsetThreadRank(me, rwlock);
 
267
#endif
 
268
    return;
 
269
}
 
270
 
 
271
/*
 
272
** Write-lock the RWLock.
 
273
*/
 
274
PR_IMPLEMENT(void)
 
275
NSSRWLock_LockWrite(NSSRWLock *rwlock)
 
276
{
 
277
    PRThread *me = PR_GetCurrentThread();
 
278
 
 
279
    PZ_Lock(rwlock->rw_lock);
 
280
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
281
    /*
 
282
     * assert that rank ordering is not violated; the rank of 'rwlock' should
 
283
     * be equal to or greater than the highest rank of all the locks held by
 
284
     * the thread.
 
285
     */
 
286
    PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
 
287
                    (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
 
288
#endif
 
289
    /*
 
290
     * wait if read locked or write locked.
 
291
     */
 
292
    PR_ASSERT(rwlock->rw_reader_locks >= 0);
 
293
    PR_ASSERT(me != NULL);
 
294
 
 
295
    UNTIL ( (rwlock->rw_owner == me) ||           /* I own write lock, or */
 
296
           ((rwlock->rw_owner == NULL) &&         /* no writer   and */
 
297
            (rwlock->rw_reader_locks == 0))) {    /* no readers, either. */
 
298
 
 
299
        rwlock->rw_waiting_writers++;
 
300
        PZ_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
 
301
        rwlock->rw_waiting_writers--;
 
302
        PR_ASSERT(rwlock->rw_reader_locks >= 0);
 
303
    }
 
304
 
 
305
    PR_ASSERT(rwlock->rw_reader_locks == 0);
 
306
    /*
 
307
     * apply write lock
 
308
     */
 
309
    rwlock->rw_owner = me;
 
310
    rwlock->rw_writer_locks++;          /* Increment write-lock count */
 
311
 
 
312
    PZ_Unlock(rwlock->rw_lock);
 
313
 
 
314
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
315
    /*
 
316
     * update thread's lock rank
 
317
     */
 
318
    nssRWLock_SetThreadRank(me,rwlock);
 
319
#endif
 
320
}
 
321
 
 
322
/* Unlock a Read lock held on this RW lock.
 
323
*/
 
324
PR_IMPLEMENT(void)
 
325
NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
 
326
{
 
327
    PRThread *me = PR_GetCurrentThread();
 
328
 
 
329
    PZ_Lock(rwlock->rw_lock);
 
330
    PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me.  */
 
331
    PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
 
332
 
 
333
    if (  rwlock->rw_owner        == me  &&     /* I own it, and            */
 
334
          rwlock->rw_writer_locks  > 0   &&     /* I own it, and            */
 
335
        --rwlock->rw_writer_locks == 0) {       /* I'm all done with it     */
 
336
 
 
337
        rwlock->rw_owner = NULL;                /* I don't own it any more. */
 
338
 
 
339
        /* Give preference to waiting writers. */
 
340
        if (rwlock->rw_waiting_writers > 0) {
 
341
            if (rwlock->rw_reader_locks == 0)
 
342
                PZ_NotifyCondVar(rwlock->rw_writer_waitq);
 
343
        } else if (rwlock->rw_waiting_readers > 0) {
 
344
            PZ_NotifyAllCondVar(rwlock->rw_reader_waitq);
 
345
        }
 
346
    }
 
347
    PZ_Unlock(rwlock->rw_lock);
 
348
 
 
349
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
350
    /*
 
351
     * update thread's lock rank
 
352
     */
 
353
    nssRWLock_UnsetThreadRank(me, rwlock);
 
354
#endif
 
355
    return;
 
356
}
 
357
 
 
358
/* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
 
359
PR_IMPLEMENT(PRBool)
 
360
NSSRWLock_HaveWriteLock(NSSRWLock *rwlock) {
 
361
    PRBool ownWriteLock;
 
362
    PRThread *me = PR_GetCurrentThread();
 
363
 
 
364
    /* This lock call isn't really necessary.
 
365
    ** If this thread is the owner, that fact cannot change during this call,
 
366
    ** because this thread is in this call.
 
367
    ** If this thread is NOT the owner, the owner could change, but it 
 
368
    ** could not become this thread.  
 
369
    */
 
370
#if UNNECESSARY
 
371
    PZ_Lock(rwlock->rw_lock);   
 
372
#endif
 
373
    ownWriteLock = (PRBool)(me == rwlock->rw_owner);
 
374
#if UNNECESSARY
 
375
    PZ_Unlock(rwlock->rw_lock);
 
376
#endif
 
377
    return ownWriteLock;
 
378
}
 
379
 
 
380
#ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
 
381
 
 
382
/*
 
383
 * nssRWLock_SetThreadRank
 
384
 *      Set a thread's lock rank, which is the highest of the ranks of all
 
385
 *      the locks held by the thread. Pointers to the locks are added to a
 
386
 *      per-thread list, which is anchored off a thread-private data key.
 
387
 */
 
388
 
 
389
static void
 
390
nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
 
391
{
 
392
    thread_rwlock_stack *lock_stack;
 
393
    PRStatus rv;
 
394
 
 
395
    /*
 
396
     * allocated thread-private-data for rwlock list, if not already allocated
 
397
     */
 
398
    if (!nss_thread_rwlock_initialized) {
 
399
        /*
 
400
         * allocate tpd, only if not failed already
 
401
         */
 
402
        if (!nss_thread_rwlock_alloc_failed) {
 
403
            if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
 
404
                                        nssRWLock_ReleaseLockStack)
 
405
                                                == PR_FAILURE) {
 
406
                nss_thread_rwlock_alloc_failed = 1;
 
407
                return;
 
408
            }
 
409
        } else
 
410
            return;
 
411
    }
 
412
    /*
 
413
     * allocate a lock stack
 
414
     */
 
415
    if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
 
416
        lock_stack = (thread_rwlock_stack *)
 
417
                        PR_CALLOC(1 * sizeof(thread_rwlock_stack));
 
418
        if (lock_stack) {
 
419
            rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
 
420
            if (rv == PR_FAILURE) {
 
421
                PR_DELETE(lock_stack);
 
422
                nss_thread_rwlock_alloc_failed = 1;
 
423
                return;
 
424
            }
 
425
        } else {
 
426
            nss_thread_rwlock_alloc_failed = 1;
 
427
            return;
 
428
        }
 
429
    }
 
430
    /*
 
431
     * add rwlock to lock stack, if limit is not exceeded
 
432
     */
 
433
    if (lock_stack) {
 
434
        if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
 
435
            lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
 
436
    }
 
437
    nss_thread_rwlock_initialized = 1;
 
438
}
 
439
 
 
440
static void
 
441
nssRWLock_ReleaseLockStack(void *lock_stack)
 
442
{
 
443
    PR_ASSERT(lock_stack);
 
444
    PR_DELETE(lock_stack);
 
445
}
 
446
 
 
447
/*
 
448
 * nssRWLock_GetThreadRank
 
449
 *
 
450
 *      return thread's lock rank. If thread-private-data for the lock
 
451
 *      stack is not allocated, return NSS_RWLOCK_RANK_NONE.
 
452
 */
 
453
 
 
454
static PRUint32
 
455
nssRWLock_GetThreadRank(PRThread *me)
 
456
{
 
457
    thread_rwlock_stack *lock_stack;
 
458
 
 
459
    if (nss_thread_rwlock_initialized) {
 
460
        if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
 
461
            return (NSS_RWLOCK_RANK_NONE);
 
462
        else
 
463
            return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
 
464
 
 
465
    } else
 
466
            return (NSS_RWLOCK_RANK_NONE);
 
467
}
 
468
 
 
469
/*
 
470
 * nssRWLock_UnsetThreadRank
 
471
 *
 
472
 *      remove the rwlock from the lock stack. Since locks may not be
 
473
 *      unlocked in a FIFO order, the entire lock stack is searched.
 
474
 */
 
475
 
 
476
static void
 
477
nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
 
478
{
 
479
    thread_rwlock_stack *lock_stack;
 
480
    int new_index = 0, index, done = 0;
 
481
 
 
482
    if (!nss_thread_rwlock_initialized)
 
483
        return;
 
484
 
 
485
    lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
 
486
 
 
487
    PR_ASSERT(lock_stack != NULL);
 
488
 
 
489
    index = lock_stack->trs_index - 1;
 
490
    while (index-- >= 0) {
 
491
        if ((lock_stack->trs_stack[index] == rwlock) && !done)  {
 
492
            /*
 
493
             * reset the slot for rwlock
 
494
             */
 
495
            lock_stack->trs_stack[index] = NULL;
 
496
            done = 1;
 
497
        }
 
498
        /*
 
499
         * search for the lowest-numbered empty slot, above which there are
 
500
         * no non-empty slots
 
501
         */
 
502
        if ((lock_stack->trs_stack[index] != NULL) && !new_index)
 
503
            new_index = index + 1;
 
504
        if (done && new_index)
 
505
            break;
 
506
    }
 
507
    /*
 
508
     * set top of stack to highest numbered empty slot
 
509
     */
 
510
    lock_stack->trs_index = new_index;
 
511
 
 
512
}
 
513
 
 
514
#endif  /* NSS_RWLOCK_RANK_ORDER_DEBUG */