~ubuntu-branches/ubuntu/karmic/firebird2.1/karmic

« back to all changes in this revision

Viewing changes to src/jrd/sch.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Damyan Ivanov
  • Date: 2008-05-26 23:59:25 UTC
  • Revision ID: james.westby@ubuntu.com-20080526235925-2pnqj6nxpppoeaer
Tags: upstream-2.1.0.17798-0.ds2
ImportĀ upstreamĀ versionĀ 2.1.0.17798-0.ds2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      PROGRAM:        JRD Access Method
 
3
 *      MODULE:         sch.cpp
 
4
 *      DESCRIPTION:    Voluntary thread scheduler
 
5
 *
 
6
 * The contents of this file are subject to the Interbase Public
 
7
 * License Version 1.0 (the "License"); you may not use this file
 
8
 * except in compliance with the License. You may obtain a copy
 
9
 * of the License at http://www.Inprise.com/IPL.html
 
10
 *
 
11
 * Software distributed under the License is distributed on an
 
12
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 
13
 * or implied. See the License for the specific language governing
 
14
 * rights and limitations under the License.
 
15
 *
 
16
 * The Original Code was created by Inprise Corporation
 
17
 * and its predecessors. Portions created by Inprise Corporation are
 
18
 * Copyright (C) Inprise Corporation.
 
19
 *
 
20
 * All Rights Reserved.
 
21
 * Contributor(s): ______________________________________.
 
22
 *
 
23
 * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
 
24
 *
 
25
 */
 
26
 
 
27
#include "firebird.h"
 
28
#include <stdio.h>
 
29
#include <stdlib.h>
 
30
#include "../jrd/common.h"
 
31
#include "../jrd/thd.h"
 
32
#include "../jrd/isc.h"
 
33
#include "../jrd/ibase.h"
 
34
#include "../jrd/gds_proto.h"
 
35
#include "../jrd/isc_s_proto.h"
 
36
#include "../jrd/sch_proto.h"
 
37
#include "../jrd/thread_proto.h"
 
38
#include "../jrd/err_proto.h"
 
39
#include "../jrd/iberr_proto.h"
 
40
#include "../jrd/gdsassert.h"
 
41
#include "../jrd/os/thd_priority.h"
 
42
#include "../common/classes/locks.h"
 
43
 
 
44
#ifdef  WIN_NT
 
45
#include <windows.h>
 
46
#endif
 
47
 
 
48
 
 
49
/* must be careful with alignment on structures like this that are
 
50
   not run through the ALL routine */
 
51
 
 
52
struct thread {
 
53
        struct thread *thread_next;     /* Next thread to be scheduled */
 
54
        struct thread *thread_prior;    /* Prior thread */
 
55
        event_t thread_stall[1];        /* Generic event to stall thread */
 
56
        FB_THREAD_ID thread_id;                 /* Current thread id */
 
57
        USHORT thread_count;            /* AST disable count */
 
58
        USHORT thread_flags;            /* Flags */
 
59
};
 
60
 
 
61
typedef thread *THREAD;
 
62
 
 
63
//  thread_flags
 
64
 
 
65
const USHORT THREAD_hiber                       = 1;    /* Thread is hibernating */
 
66
const USHORT THREAD_ast_disabled        = 2;    /* Disable AST delivery */
 
67
const USHORT THREAD_ast_active          = 4;    /* Disable AST preemption while AST active */
 
68
const USHORT THREAD_ast_pending         = 8;    /* AST waiting to be delivered */
 
69
 
 
70
static THREAD alloc_thread(void);
 
71
static bool ast_enable(void);
 
72
static void ast_disable(void);
 
73
static void cleanup(void *);
 
74
static void mutex_bugcheck(const TEXT*, int);
 
75
static bool schedule(void);
 
76
static bool schedule_active(bool);
 
77
static void stall(THREAD);
 
78
static void stall_ast(THREAD);
 
79
static void sch_mutex_lock(Firebird::Mutex&);
 
80
static void sch_mutex_unlock(Firebird::Mutex&);
 
81
 
 
82
static THREAD free_threads = NULL;
 
83
static THREAD active_thread = NULL;
 
84
static THREAD ast_thread = NULL;
 
85
static Firebird::Mutex thread_mutex;
 
86
volatile static bool init_flag = false;
 
87
static USHORT enabled = FALSE;
 
88
 
 
89
 
 
90
#ifdef VMS
 
91
int API_ROUTINE gds__ast_active(void)
 
92
{
 
93
/**************************************
 
94
 *
 
95
 *      g d s _ $ a s t _ a c t i v e
 
96
 *
 
97
 **************************************
 
98
 *
 
99
 * Functional description
 
100
 *      An asynchronous operation has completed, wake somebody
 
101
 *      up, if necessary.
 
102
 *
 
103
 **************************************/
 
104
 
 
105
        return THREAD_ast_active();
 
106
}
 
107
 
 
108
 
 
109
void API_ROUTINE gds__completion_ast(int *arg)
 
110
{
 
111
/**************************************
 
112
 *
 
113
 *      g d s _ $ c o m p l e t i o n _ a s t
 
114
 *
 
115
 **************************************
 
116
 *
 
117
 * Functional description
 
118
 *      An asynchronous operation has completed, wake somebody
 
119
 *      up, if necessary.
 
120
 *
 
121
 **************************************/
 
122
 
 
123
        THREAD_completion_ast();
 
124
        sys$wake(0, 0);
 
125
}
 
126
#endif
 
127
 
 
128
 
 
129
int API_ROUTINE gds__thread_enable(int enable_flag)
 
130
{
 
131
/**************************************
 
132
 *
 
133
 *      g d s _ $ t h r e a d _ e n a b l e
 
134
 *
 
135
 **************************************
 
136
 *
 
137
 * Functional description
 
138
 *      Check-in with thread traffic cop.
 
139
 *
 
140
 **************************************/
 
141
 
 
142
        if (enable_flag) {
 
143
                enabled = TRUE;
 
144
                SCH_init();
 
145
        }
 
146
 
 
147
        return enabled;
 
148
}
 
149
 
 
150
 
 
151
void API_ROUTINE gds__thread_enter(void)
 
152
{
 
153
/**************************************
 
154
 *
 
155
 *      g d s _ $ t h r e a d _ e n t e r
 
156
 *
 
157
 **************************************
 
158
 *
 
159
 * Functional description
 
160
 *      Check-in with thread traffic cop.
 
161
 *
 
162
 **************************************/
 
163
 
 
164
        SCH_enter();
 
165
}
 
166
 
 
167
 
 
168
void API_ROUTINE gds__thread_exit(void)
 
169
{
 
170
/**************************************
 
171
 *
 
172
 *      g d s _ $ t h r e a d _ e x i t
 
173
 *
 
174
 **************************************
 
175
 *
 
176
 * Functional description
 
177
 *      Check-out with thread traffic cop.
 
178
 *
 
179
 **************************************/
 
180
 
 
181
        SCH_exit();
 
182
}
 
183
 
 
184
 
 
185
#ifdef VMS
 
186
int API_ROUTINE gds__thread_wait(FPTR_INT entrypoint, SLONG arg)
 
187
{
 
188
/**************************************
 
189
 *
 
190
 *      g d s _ $ t h r e a d _ w a i t
 
191
 *
 
192
 **************************************
 
193
 *
 
194
 * Functional description
 
195
 *      Stall a thread with a callback to determine runnability.
 
196
 *
 
197
 **************************************/
 
198
 
 
199
        return thread_wait(entrypoint, arg);
 
200
}
 
201
#endif
 
202
 
 
203
 
 
204
void SCH_abort(void)
 
205
{
 
206
/**************************************
 
207
 *
 
208
 *      S C H _ a b o r t
 
209
 *
 
210
 **************************************
 
211
 *
 
212
 * Functional description
 
213
 *      Try to abort the current thread.  If the thread is active,
 
214
 *      unlink it.
 
215
 *
 
216
 **************************************/
 
217
 
 
218
/* If threading isn't active, don't sweat it */
 
219
 
 
220
        if (!active_thread)
 
221
                return;
 
222
 
 
223
/* See if we can find thread.  If not, don't worry about it */
 
224
 
 
225
        const FB_THREAD_ID id = ThreadData::getId();
 
226
        THREAD thread;
 
227
        for (THREAD* ptr = &active_thread; thread = *ptr; ptr = &thread->thread_next)
 
228
        {
 
229
                if (thread->thread_id == id)
 
230
                        break;
 
231
                if (thread->thread_next == active_thread)
 
232
                        return;
 
233
        }
 
234
 
 
235
/* If we're the active thread, do a normal thread exit */
 
236
 
 
237
        if (thread == active_thread) {
 
238
                SCH_exit();
 
239
                return;
 
240
        }
 
241
 
 
242
/* We're on the list but not active.  Remove from list */
 
243
 
 
244
        sch_mutex_lock(thread_mutex);
 
245
        thread->thread_prior->thread_next = thread->thread_next;
 
246
        thread->thread_next->thread_prior = thread->thread_prior;
 
247
        thread->thread_next = free_threads;
 
248
        free_threads = thread;
 
249
        sch_mutex_unlock(thread_mutex);
 
250
}
 
251
 
 
252
 
 
253
void SCH_ast(enum ast_t action)
 
254
{
 
255
/**************************************
 
256
 *
 
257
 *      S C H _ a s t
 
258
 *
 
259
 **************************************
 
260
 *
 
261
 * Functional description
 
262
 *      Control AST delivery for threaded platforms.
 
263
 *      In particular, make AST delivery non-preemptible
 
264
 *      to avoid nasty race conditions.
 
265
 *
 
266
 *      In case you're wondering: AST = Asynchronous System Trap
 
267
 *
 
268
 **************************************/
 
269
        if (!ast_thread && !(action == AST_alloc || action == AST_disable ||
 
270
                                                 action == AST_enable))
 
271
        {
 
272
                /* Better be an AST thread before we do anything to it! */
 
273
                fb_assert(FALSE);
 
274
                return;
 
275
        }
 
276
 
 
277
        if (ast_thread && action == AST_check)
 
278
                if (!(ast_thread->thread_flags & THREAD_ast_pending) ||
 
279
                        ast_thread->thread_count > 1)
 
280
                {
 
281
                        return;
 
282
                }
 
283
 
 
284
        if (!init_flag)
 
285
                SCH_init();
 
286
 
 
287
        sch_mutex_lock(thread_mutex);
 
288
 
 
289
        switch (action) {
 
290
                /* Check into thread scheduler as AST thread */
 
291
 
 
292
        case AST_alloc:
 
293
                ast_thread = alloc_thread();
 
294
                ast_thread->thread_flags = THREAD_ast_disabled;
 
295
                ast_thread->thread_prior = ast_thread->thread_next = ast_thread;
 
296
                break;
 
297
 
 
298
        case AST_init:
 
299
                ast_thread->thread_id = ThreadData::getId();
 
300
                break;
 
301
 
 
302
                /* Check out of thread scheduler as AST thread */
 
303
 
 
304
        case AST_fini:
 
305
                ast_thread->thread_next = free_threads;
 
306
                free_threads = ast_thread;
 
307
                ast_thread = NULL;
 
308
                break;
 
309
 
 
310
                /* Disable AST delivery if not an AST thread */
 
311
 
 
312
        case AST_disable:
 
313
                ast_disable();
 
314
                break;
 
315
 
 
316
                /* Reenable AST delivery if not an AST thread */
 
317
 
 
318
        case AST_enable:
 
319
                ast_enable();
 
320
                break;
 
321
 
 
322
                /* Active thread allows a pending AST to be delivered
 
323
                   and waits */
 
324
 
 
325
        case AST_check:
 
326
                if (ast_enable())
 
327
                        stall(active_thread);
 
328
                else
 
329
                        ast_disable();
 
330
                break;
 
331
 
 
332
                /* Request AST delivery and prevent thread scheduling */
 
333
 
 
334
        case AST_enter:
 
335
                if (ast_thread->thread_flags & THREAD_ast_disabled) {
 
336
                        ast_thread->thread_flags |= THREAD_ast_pending;
 
337
                        stall_ast(ast_thread);
 
338
                }
 
339
                ast_thread->thread_flags |= THREAD_ast_active;
 
340
                break;
 
341
 
 
342
                /* AST delivery complete; resume thread scheduling */
 
343
 
 
344
        case AST_exit:
 
345
                ast_thread->thread_flags &= ~(THREAD_ast_active | THREAD_ast_pending);
 
346
                if (active_thread)
 
347
                        ISC_event_post(active_thread->thread_stall);
 
348
 
 
349
                /* Post non-active threads that have requested AST disabling */
 
350
 
 
351
                for (THREAD thread = ast_thread->thread_next; thread != ast_thread;
 
352
                         thread = thread->thread_next)
 
353
                {
 
354
                         ISC_event_post(thread->thread_stall);
 
355
                }
 
356
                break;
 
357
        }
 
358
 
 
359
        sch_mutex_unlock(thread_mutex);
 
360
}
 
361
 
 
362
 
 
363
THREAD SCH_current_thread(void)
 
364
{
 
365
/**************************************
 
366
 *
 
367
 *      S C H _ c u r r e n t _ t h r e a d
 
368
 *
 
369
 **************************************
 
370
 *
 
371
 * Functional description
 
372
 *      Return id of current thread.  If scheduling is not active,
 
373
 *      return NULL.
 
374
 *
 
375
 **************************************/
 
376
 
 
377
        return active_thread;
 
378
}
 
379
 
 
380
 
 
381
void SCH_enter(void)
 
382
{
 
383
/**************************************
 
384
 *
 
385
 *      S C H _ e n t e r
 
386
 *
 
387
 **************************************
 
388
 *
 
389
 * Functional description
 
390
 *      A thread wants to enter the access method and submit to our scheduling.
 
391
 *      Humor him.
 
392
 *
 
393
 **************************************/
 
394
 
 
395
/* Special case single thread case */
 
396
 
 
397
#ifndef MULTI_THREAD
 
398
        if (free_threads) {
 
399
                THREAD thread = active_thread = free_threads;
 
400
                free_threads = NULL;
 
401
                thread->thread_next = thread->thread_prior = thread;
 
402
                thread->thread_flags = 0;
 
403
                thread->thread_id = ThreadData::getId();
 
404
                return;
 
405
        }
 
406
#endif
 
407
 
 
408
        if (!init_flag) {
 
409
                SCH_init();
 
410
        }
 
411
 
 
412
/* Get mutex on scheduler data structures to prevent tragic misunderstandings */
 
413
 
 
414
        sch_mutex_lock(thread_mutex);
 
415
 
 
416
        THREAD thread = alloc_thread();
 
417
        thread->thread_id = ThreadData::getId();
 
418
 
 
419
/* Link thread block into circular list of active threads */
 
420
 
 
421
        if (active_thread)
 
422
        {
 
423
                /* The calling thread should NOT be the active_thread 
 
424
                   This is to prevent deadlock by the same thread */
 
425
                fb_assert(thread->thread_id != active_thread->thread_id);
 
426
 
 
427
                thread->thread_next = active_thread;
 
428
                THREAD prior = active_thread->thread_prior;
 
429
                thread->thread_prior = prior;
 
430
                active_thread->thread_prior = thread;
 
431
                prior->thread_next = thread;
 
432
        }
 
433
        else
 
434
        {
 
435
                thread->thread_next = thread->thread_prior = thread;
 
436
                active_thread = thread;
 
437
        }
 
438
        ThreadPriorityScheduler::enter();
 
439
 
 
440
        if (active_thread->thread_flags & THREAD_hiber) {
 
441
                schedule();
 
442
        }
 
443
 
 
444
        stall(thread);
 
445
        sch_mutex_unlock(thread_mutex);
 
446
}
 
447
 
 
448
 
 
449
void SCH_exit(void)
 
450
{
 
451
/**************************************
 
452
 *
 
453
 *      S C H _ e x i t
 
454
 *
 
455
 **************************************
 
456
 *
 
457
 * Functional description
 
458
 *      Thread is done in access method, remove data structure from
 
459
 *      scheduler, and release thread block.
 
460
 *
 
461
 **************************************/
 
462
        SCH_validate();
 
463
 
 
464
#ifndef MULTI_THREAD
 
465
        free_threads = active_thread;
 
466
        active_thread = NULL;
 
467
        free_threads->thread_next = NULL;
 
468
#else
 
469
        sch_mutex_lock(thread_mutex);
 
470
 
 
471
        ast_enable();                           /* Reenable AST delivery */
 
472
 
 
473
        THREAD thread = active_thread;
 
474
 
 
475
        // This is to prevent nasty crash if error (for example, IO error) 
 
476
        // happens during attach to database in SS builds. Exception
 
477
        // handler there calls THREAD_EXIT without preceding THREAD_ENTER
 
478
        // in this case (during shutdown of CACHE_WRITER or CACHE_READER)
 
479
        if (!thread) {
 
480
                sch_mutex_unlock(thread_mutex);
 
481
                return; 
 
482
        }
 
483
 
 
484
        if (thread == thread->thread_next)
 
485
                active_thread = NULL;
 
486
        else {
 
487
                THREAD next = thread->thread_next;
 
488
                THREAD prior = thread->thread_prior;
 
489
                active_thread = prior;
 
490
                prior->thread_next = next;
 
491
                next->thread_prior = prior;
 
492
        }
 
493
        ThreadPriorityScheduler::exit();
 
494
 
 
495
        thread->thread_next = free_threads;
 
496
        free_threads = thread;
 
497
        schedule();
 
498
 
 
499
        sch_mutex_unlock(thread_mutex);
 
500
#endif
 
501
}
 
502
 
 
503
 
 
504
void SCH_hiber(void)
 
505
{
 
506
/**************************************
 
507
 *
 
508
 *      S C H _ h i b e r 
 
509
 *
 
510
 **************************************
 
511
 *
 
512
 * Functional description
 
513
 *      Go to sleep until woken by another thread.  See also
 
514
 *      SCH_wake.
 
515
 *
 
516
 **************************************/
 
517
 
 
518
        schedule_active(true);
 
519
}
 
520
 
 
521
#ifdef MULTI_THREAD
 
522
static Firebird::Mutex scheduler_init_lock;
 
523
#endif
 
524
 
 
525
void SCH_init(void)
 
526
{
 
527
/**************************************
 
528
 *
 
529
 *      S C H _ i n i t
 
530
 *
 
531
 **************************************
 
532
 *
 
533
 * Functional description
 
534
 *      Initialize the thread scheduler.
 
535
 *
 
536
 **************************************/
 
537
        if (init_flag)
 
538
                return;
 
539
                
 
540
#ifdef MULTI_THREAD
 
541
        scheduler_init_lock.enter();
 
542
 
 
543
        try {
 
544
                if (!init_flag) {
 
545
#endif
 
546
                        gds__register_cleanup(cleanup, 0);
 
547
                        init_flag = true;
 
548
#ifdef MULTI_THREAD
 
549
                }
 
550
        }
 
551
        catch (const Firebird::Exception&) {
 
552
                scheduler_init_lock.leave();
 
553
                throw;
 
554
        }
 
555
        scheduler_init_lock.leave();
 
556
#endif
 
557
}
 
558
 
 
559
 
 
560
bool SCH_schedule(void)
 
561
{
 
562
/**************************************
 
563
 *
 
564
 *      S C H _ s c h e d u l e
 
565
 *
 
566
 **************************************
 
567
 *
 
568
 * Functional description
 
569
 *      Voluntarily relinquish control so that others may run.
 
570
 *      If a context switch actually happened, return true.
 
571
 *
 
572
 **************************************/
 
573
        return schedule_active(false);
 
574
}
 
575
 
 
576
 
 
577
bool SCH_thread_enter_check(void)
 
578
{
 
579
/**************************************
 
580
 *
 
581
 *      S C H _ t h r e a d _ e n t e r _ c h e c k
 
582
 *
 
583
 **************************************
 
584
 *
 
585
 * Functional description
 
586
 *      Check if thread is active thread, if so return true
 
587
 * else return false.
 
588
 *
 
589
 **************************************/
 
590
 
 
591
/* if active thread is not null and thread_id matches the we are the
 
592
   active thread */
 
593
        sch_mutex_lock(thread_mutex);
 
594
        const bool ret = ((active_thread) && (active_thread->thread_id == ThreadData::getId()));
 
595
        sch_mutex_unlock(thread_mutex);
 
596
 
 
597
        return ret;
 
598
}
 
599
 
 
600
 
 
601
bool SCH_validate(void)
 
602
{
 
603
/**************************************
 
604
 *
 
605
 *      S C H _ v a l i d a t e
 
606
 *
 
607
 **************************************
 
608
 *
 
609
 * Functional description
 
610
 *      Check integrity of thread system (assume thread is entered).
 
611
 *
 
612
 **************************************/
 
613
 
 
614
        if (!init_flag || !active_thread) {
 
615
                gds__log("SCH_validate -- not entered");
 
616
                // CVC: No need to replace by fb_utils::readenv() I think.
 
617
                if (getenv("ISC_PUNT")) 
 
618
                        abort();
 
619
                return false;
 
620
        }
 
621
 
 
622
#ifdef MULTI_THREAD
 
623
        if (active_thread->thread_id != ThreadData::getId()) {
 
624
                gds__log("SCH_validate -- wrong thread");
 
625
                return false;
 
626
        }
 
627
#endif
 
628
 
 
629
        return true;
 
630
}
 
631
 
 
632
 
 
633
void SCH_wake(THREAD thread)
 
634
{
 
635
/**************************************
 
636
 *
 
637
 *      S C H _ w a k e
 
638
 *
 
639
 **************************************
 
640
 *
 
641
 * Functional description
 
642
 *      Take thread out of hibernation.
 
643
 *
 
644
 **************************************/
 
645
        thread->thread_flags &= ~THREAD_hiber;
 
646
        ISC_event_post(thread->thread_stall);
 
647
}
 
648
 
 
649
 
 
650
static THREAD alloc_thread(void)
 
651
{
 
652
/**************************************
 
653
 *
 
654
 *      a l l o c _ t h r e a d
 
655
 *
 
656
 **************************************
 
657
 *
 
658
 * Functional description
 
659
 *      Allocate a thread block.
 
660
 *
 
661
 **************************************/
 
662
 
 
663
/* Find a useable thread block.  If there isn't one, allocate one */
 
664
 
 
665
        THREAD thread = free_threads;
 
666
        if (thread)
 
667
                free_threads = thread->thread_next;
 
668
        else {
 
669
                thread = (THREAD) gds__alloc((SLONG) sizeof(struct thread));
 
670
                /* FREE: unknown */
 
671
                if (!thread)                    /* NOMEM: bugcheck */
 
672
                        mutex_bugcheck("Out of memory", 0);     /* no real error handling */
 
673
#ifdef DEBUG_GDS_ALLOC
 
674
                /* There is one thread structure allocated per thread, and this
 
675
                 * is never freed except by process exit
 
676
                 */
 
677
                gds_alloc_flag_unfreed((void *) thread);
 
678
#endif
 
679
                ISC_event_init(thread->thread_stall, 0, 0);
 
680
        }
 
681
 
 
682
        thread->thread_flags = thread->thread_count = 0;
 
683
        return thread;
 
684
}
 
685
 
 
686
 
 
687
static bool ast_enable(void)
 
688
{
 
689
/**************************************
 
690
 *
 
691
 *      a s t _ e n a b l e
 
692
 *
 
693
 **************************************
 
694
 *
 
695
 * Functional description
 
696
 *      Enables AST delivery and returns
 
697
 *      TRUE if an AST is deliverable.
 
698
 *
 
699
 **************************************/
 
700
        if (!ast_thread)
 
701
                return false;
 
702
 
 
703
        if (ast_thread->thread_flags & THREAD_ast_active &&
 
704
                ast_thread->thread_id == ThreadData::getId())
 
705
        {
 
706
                return false;
 
707
        }
 
708
 
 
709
        if (!ast_thread->thread_count || !--ast_thread->thread_count) {
 
710
                ast_thread->thread_flags &= ~THREAD_ast_disabled;
 
711
                if (ast_thread->thread_flags & THREAD_ast_pending) {
 
712
                        ast_thread->thread_flags |= THREAD_ast_active;
 
713
                        ISC_event_post(ast_thread->thread_stall);
 
714
                        return true;
 
715
                }
 
716
        }
 
717
 
 
718
        return false;
 
719
}
 
720
 
 
721
 
 
722
static void ast_disable(void)
 
723
{
 
724
/**************************************
 
725
 *
 
726
 *      a s t _ d i s a b l e
 
727
 *
 
728
 **************************************
 
729
 *
 
730
 * Functional description
 
731
 *      Disables AST delivery and waits
 
732
 *      until an active AST completes
 
733
 *      before returning.
 
734
 *
 
735
 **************************************/
 
736
        if (!ast_thread)
 
737
                return;
 
738
 
 
739
        if (ast_thread->thread_flags & THREAD_ast_active) {
 
740
                if (ast_thread->thread_id == ThreadData::getId())
 
741
                        return;
 
742
                else {
 
743
                        if (active_thread
 
744
                                && active_thread->thread_id == ThreadData::getId()) 
 
745
                        {
 
746
                                stall(active_thread);
 
747
                                return;
 
748
                        }
 
749
                        else {
 
750
                                THREAD thread = alloc_thread();
 
751
                                stall_ast(thread);
 
752
                                thread->thread_next = free_threads;
 
753
                                free_threads = thread;
 
754
                        }
 
755
                }
 
756
        }
 
757
 
 
758
        ast_thread->thread_flags |= THREAD_ast_disabled;
 
759
        ++ast_thread->thread_count;
 
760
}
 
761
 
 
762
 
 
763
static void cleanup(void *arg)
 
764
{
 
765
/**************************************
 
766
 *
 
767
 *      c l e a n u p
 
768
 *
 
769
 **************************************
 
770
 *
 
771
 * Functional description
 
772
 *      Exit handler for image exit.
 
773
 *
 
774
 **************************************/
 
775
        if (!init_flag)
 
776
                return;
 
777
 
 
778
/* this is added to make sure that we release the memory
 
779
 * we have allocated for the thread event handler through
 
780
 * ISC_event_handle () (CreateEvent) */
 
781
 
 
782
#ifdef SUPERCLIENT
 
783
/* use locks */
 
784
        thread_mutex.enter();
 
785
 
 
786
        if (!init_flag)
 
787
                return;
 
788
 
 
789
/* loop through the list of active threads and free the events */
 
790
        THREAD temp_thread = active_thread;
 
791
        if (temp_thread) {
 
792
                /* reach to the starting of the list */
 
793
                while (temp_thread != temp_thread->thread_prior)
 
794
                        temp_thread = temp_thread->thread_prior;
 
795
                /* now we are at the begining of the list. Now start freeing the
 
796
                 * EVENT structures and move to the next */
 
797
                do {
 
798
                        ISC_event_fini(temp_thread->thread_stall);
 
799
                        /* the thread structures are freed as a part of the 
 
800
                         * gds_alloc cleanup, so do not worry about them here
 
801
                         */
 
802
                } while (temp_thread->thread_next != temp_thread
 
803
                           && (temp_thread = temp_thread->thread_next));
 
804
 
 
805
        }
 
806
 
 
807
/* loop through the list of free threads and free the events */
 
808
        if (temp_thread = free_threads) {
 
809
                /* reach to the starting of the list */
 
810
                while (temp_thread != temp_thread->thread_prior)
 
811
                        temp_thread = temp_thread->thread_prior;
 
812
                /* now we are at the begining of the list. Now start freeing the
 
813
                 * EVENT structures and move to the next */
 
814
                do {
 
815
                        ISC_event_fini(temp_thread->thread_stall);
 
816
                        /* the thread structures are freed as a part of the 
 
817
                         * gds_alloc cleanup, so do not worry about them here
 
818
                         */
 
819
                } while (temp_thread->thread_next != temp_thread
 
820
                           && (temp_thread = temp_thread->thread_next));
 
821
 
 
822
 
 
823
        }
 
824
 
 
825
        thread_mutex.leave();
 
826
#endif /* SUPERCLIENT */
 
827
 
 
828
        init_flag = false;
 
829
}
 
830
 
 
831
 
 
832
static void mutex_bugcheck(const TEXT* string, int mutex_state)
 
833
{
 
834
/**************************************
 
835
 *
 
836
 *      m u t e x _ b u g c h e c k
 
837
 *
 
838
 **************************************
 
839
 *
 
840
 * Functional description
 
841
 *      There has been a bugcheck during a mutex operation.
 
842
 *      Post the bugcheck.
 
843
 *
 
844
 **************************************/
 
845
        TEXT msg[128];
 
846
 
 
847
        sprintf(msg, "SCH: %.93s error, status = %d", string, mutex_state);
 
848
        gds__log(msg);
 
849
        fprintf(stderr, "%s\n", msg);
 
850
 
 
851
        abort();
 
852
}
 
853
 
 
854
 
 
855
// CVC: Nobody checks the result from this function.
 
856
static bool schedule(void)
 
857
{
 
858
/**************************************
 
859
 *
 
860
 *      s c h e d u l e
 
861
 *
 
862
 **************************************
 
863
 *
 
864
 * Functional description
 
865
 *      Loop thru active thread to find the next runable task.  If we find one,
 
866
 *      set "active_tasks" to point to it and return true.  Otherwise simply
 
867
 *      return false.
 
868
 *
 
869
 **************************************/
 
870
        if (!active_thread)
 
871
                return false;
 
872
 
 
873
        THREAD thread = active_thread;
 
874
 
 
875
        for (;;) {
 
876
                thread = thread->thread_next;
 
877
                if (!(thread->thread_flags & THREAD_hiber))
 
878
                        break;
 
879
                if (thread == active_thread)
 
880
                        return false;
 
881
        }
 
882
 
 
883
        active_thread = thread;
 
884
        ISC_event_post(active_thread->thread_stall);
 
885
 
 
886
        return true;
 
887
}
 
888
 
 
889
 
 
890
static bool schedule_active(bool hiber_flag)
 
891
{
 
892
/**************************************
 
893
 *
 
894
 *      s c h e d u l e _ a c t i v e
 
895
 *
 
896
 **************************************
 
897
 *
 
898
 * Functional description
 
899
 *      Voluntarily relinquish control so that others may run.
 
900
 *      If a context switch actually happened, return true.
 
901
 *
 
902
 **************************************/
 
903
#ifndef MULTI_THREAD
 
904
        return false;
 
905
#else
 
906
        if (!active_thread)
 
907
                return false;
 
908
 
 
909
        sch_mutex_lock(thread_mutex);
 
910
 
 
911
/* Take this opportunity to check for pending ASTs
 
912
   and deliver them. */
 
913
 
 
914
        if (ast_enable())
 
915
                stall(active_thread);
 
916
        else
 
917
                ast_disable();
 
918
 
 
919
        if (hiber_flag)
 
920
                active_thread->thread_flags |= THREAD_hiber;
 
921
        THREAD thread = active_thread;
 
922
        schedule();
 
923
        bool ret;
 
924
        if (thread == active_thread && !(thread->thread_flags & THREAD_hiber))
 
925
                ret = false;
 
926
        else {
 
927
                ast_enable();
 
928
                stall(thread);
 
929
                ret = true;
 
930
        }
 
931
 
 
932
        sch_mutex_unlock(thread_mutex);
 
933
 
 
934
        return ret;
 
935
#endif
 
936
}
 
937
 
 
938
 
 
939
static void stall(THREAD thread)
 
940
{
 
941
/**************************************
 
942
 *
 
943
 *      s t a l l
 
944
 *
 
945
 **************************************
 
946
 *
 
947
 * Functional description
 
948
 *      Stall until our thread is made active.
 
949
 *
 
950
 **************************************/
 
951
        if (thread != active_thread || thread->thread_flags & THREAD_hiber ||
 
952
                (ast_thread && ast_thread->thread_flags & THREAD_ast_active))
 
953
        {
 
954
                for (;;) {
 
955
                        SLONG value = ISC_event_clear(thread->thread_stall);
 
956
                        if (thread == active_thread
 
957
                                && !(thread->thread_flags & THREAD_hiber)
 
958
                                && (!ast_thread
 
959
                                        || !(ast_thread->thread_flags & THREAD_ast_active)))
 
960
                        {
 
961
                                break;
 
962
                        }
 
963
                        sch_mutex_unlock(thread_mutex);
 
964
                        event_t* ptr = thread->thread_stall;
 
965
                        ISC_event_wait(1, &ptr, &value, 0, 0, 0);
 
966
                        sch_mutex_lock(thread_mutex);
 
967
                }
 
968
        }
 
969
 
 
970
/* Explicitly disable AST delivery for active thread */
 
971
 
 
972
        ast_disable();
 
973
}
 
974
 
 
975
 
 
976
static void stall_ast(THREAD thread)
 
977
{
 
978
/**************************************
 
979
 *
 
980
 *      s t a l l _ a s t
 
981
 *
 
982
 **************************************
 
983
 *
 
984
 * Functional description
 
985
 *      If this the AST thread then stall until AST delivery
 
986
 *      is reenabled. Otherwise, this is a normal thread (though
 
987
 *      not the active thread) and it wants to wait until
 
988
 *      AST is complete.
 
989
 *
 
990
 **************************************/
 
991
        if (thread == ast_thread) {
 
992
                if (ast_thread->thread_flags & THREAD_ast_disabled)
 
993
                        for (;;) {
 
994
                                SLONG value = ISC_event_clear(thread->thread_stall);
 
995
                                if (!(ast_thread->thread_flags & THREAD_ast_disabled))
 
996
                                        break;
 
997
                                sch_mutex_unlock(thread_mutex);
 
998
                                event_t* ptr = thread->thread_stall;
 
999
                                ISC_event_wait(1, &ptr, &value, 0, 0, 0);
 
1000
                                sch_mutex_lock(thread_mutex);
 
1001
                        }
 
1002
        }
 
1003
        else {
 
1004
                /* Link thread block into ast thread queue */
 
1005
 
 
1006
                thread->thread_next = ast_thread->thread_next;
 
1007
                thread->thread_prior = ast_thread;
 
1008
                ast_thread->thread_next->thread_prior = thread;
 
1009
                ast_thread->thread_next = thread;
 
1010
 
 
1011
                /* Wait for AST delivery to complete */
 
1012
 
 
1013
                if (ast_thread->thread_flags & THREAD_ast_active)
 
1014
                        for (;;) {
 
1015
                                SLONG value = ISC_event_clear(thread->thread_stall);
 
1016
                                if (!(ast_thread->thread_flags & THREAD_ast_active))
 
1017
                                        break;
 
1018
                                sch_mutex_unlock(thread_mutex);
 
1019
                                event_t* ptr = thread->thread_stall;
 
1020
                                ISC_event_wait(1, &ptr, &value, 0, 0, 0);
 
1021
                                sch_mutex_lock(thread_mutex);
 
1022
                        }
 
1023
                /* Unlink thread block from ast thread queue */
 
1024
 
 
1025
                thread->thread_prior->thread_next = thread->thread_next;
 
1026
                thread->thread_next->thread_prior = thread->thread_prior;
 
1027
        }
 
1028
}
 
1029
 
 
1030
 
 
1031
static void sch_mutex_lock(Firebird::Mutex& mtx)
 
1032
{
 
1033
/**************************************
 
1034
 *
 
1035
 *      s c h _ m u t e x _ l o c k
 
1036
 *
 
1037
 **************************************
 
1038
 *
 
1039
 * Functional description
 
1040
 *      Enters mutex, on error bugcheks.
 
1041
 *
 
1042
 **************************************/
 
1043
        try
 
1044
        {
 
1045
                mtx.enter();
 
1046
        }
 
1047
        catch (const Firebird::system_call_failed& e)
 
1048
        {
 
1049
                mutex_bugcheck("mutex lock", e.getErrorCode());
 
1050
        }
 
1051
}
 
1052
 
 
1053
 
 
1054
static void sch_mutex_unlock(Firebird::Mutex& mtx)
 
1055
{
 
1056
/**************************************
 
1057
 *
 
1058
 *      s c h _ m u t e x _ u n l o c k
 
1059
 *
 
1060
 **************************************
 
1061
 *
 
1062
 * Functional description
 
1063
 *      Leaves mutex, on error bugcheks.
 
1064
 *
 
1065
 **************************************/
 
1066
        try
 
1067
        {
 
1068
                mtx.leave();
 
1069
        }
 
1070
        catch (const Firebird::system_call_failed& e)
 
1071
        {
 
1072
                mutex_bugcheck("mutex unlock", e.getErrorCode());
 
1073
        }
 
1074
}