2
* PROGRAM: JRD Access Method
4
* DESCRIPTION: Voluntary thread scheduler
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
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.
16
* The Original Code was created by Inprise Corporation
17
* and its predecessors. Portions created by Inprise Corporation are
18
* Copyright (C) Inprise Corporation.
20
* All Rights Reserved.
21
* Contributor(s): ______________________________________.
23
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
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"
49
/* must be careful with alignment on structures like this that are
50
not run through the ALL routine */
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 */
61
typedef thread *THREAD;
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 */
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&);
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;
91
int API_ROUTINE gds__ast_active(void)
93
/**************************************
95
* g d s _ $ a s t _ a c t i v e
97
**************************************
99
* Functional description
100
* An asynchronous operation has completed, wake somebody
103
**************************************/
105
return THREAD_ast_active();
109
void API_ROUTINE gds__completion_ast(int *arg)
111
/**************************************
113
* g d s _ $ c o m p l e t i o n _ a s t
115
**************************************
117
* Functional description
118
* An asynchronous operation has completed, wake somebody
121
**************************************/
123
THREAD_completion_ast();
129
int API_ROUTINE gds__thread_enable(int enable_flag)
131
/**************************************
133
* g d s _ $ t h r e a d _ e n a b l e
135
**************************************
137
* Functional description
138
* Check-in with thread traffic cop.
140
**************************************/
151
void API_ROUTINE gds__thread_enter(void)
153
/**************************************
155
* g d s _ $ t h r e a d _ e n t e r
157
**************************************
159
* Functional description
160
* Check-in with thread traffic cop.
162
**************************************/
168
void API_ROUTINE gds__thread_exit(void)
170
/**************************************
172
* g d s _ $ t h r e a d _ e x i t
174
**************************************
176
* Functional description
177
* Check-out with thread traffic cop.
179
**************************************/
186
int API_ROUTINE gds__thread_wait(FPTR_INT entrypoint, SLONG arg)
188
/**************************************
190
* g d s _ $ t h r e a d _ w a i t
192
**************************************
194
* Functional description
195
* Stall a thread with a callback to determine runnability.
197
**************************************/
199
return thread_wait(entrypoint, arg);
206
/**************************************
210
**************************************
212
* Functional description
213
* Try to abort the current thread. If the thread is active,
216
**************************************/
218
/* If threading isn't active, don't sweat it */
223
/* See if we can find thread. If not, don't worry about it */
225
const FB_THREAD_ID id = ThreadData::getId();
227
for (THREAD* ptr = &active_thread; thread = *ptr; ptr = &thread->thread_next)
229
if (thread->thread_id == id)
231
if (thread->thread_next == active_thread)
235
/* If we're the active thread, do a normal thread exit */
237
if (thread == active_thread) {
242
/* We're on the list but not active. Remove from list */
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);
253
void SCH_ast(enum ast_t action)
255
/**************************************
259
**************************************
261
* Functional description
262
* Control AST delivery for threaded platforms.
263
* In particular, make AST delivery non-preemptible
264
* to avoid nasty race conditions.
266
* In case you're wondering: AST = Asynchronous System Trap
268
**************************************/
269
if (!ast_thread && !(action == AST_alloc || action == AST_disable ||
270
action == AST_enable))
272
/* Better be an AST thread before we do anything to it! */
277
if (ast_thread && action == AST_check)
278
if (!(ast_thread->thread_flags & THREAD_ast_pending) ||
279
ast_thread->thread_count > 1)
287
sch_mutex_lock(thread_mutex);
290
/* Check into thread scheduler as AST thread */
293
ast_thread = alloc_thread();
294
ast_thread->thread_flags = THREAD_ast_disabled;
295
ast_thread->thread_prior = ast_thread->thread_next = ast_thread;
299
ast_thread->thread_id = ThreadData::getId();
302
/* Check out of thread scheduler as AST thread */
305
ast_thread->thread_next = free_threads;
306
free_threads = ast_thread;
310
/* Disable AST delivery if not an AST thread */
316
/* Reenable AST delivery if not an AST thread */
322
/* Active thread allows a pending AST to be delivered
327
stall(active_thread);
332
/* Request AST delivery and prevent thread scheduling */
335
if (ast_thread->thread_flags & THREAD_ast_disabled) {
336
ast_thread->thread_flags |= THREAD_ast_pending;
337
stall_ast(ast_thread);
339
ast_thread->thread_flags |= THREAD_ast_active;
342
/* AST delivery complete; resume thread scheduling */
345
ast_thread->thread_flags &= ~(THREAD_ast_active | THREAD_ast_pending);
347
ISC_event_post(active_thread->thread_stall);
349
/* Post non-active threads that have requested AST disabling */
351
for (THREAD thread = ast_thread->thread_next; thread != ast_thread;
352
thread = thread->thread_next)
354
ISC_event_post(thread->thread_stall);
359
sch_mutex_unlock(thread_mutex);
363
THREAD SCH_current_thread(void)
365
/**************************************
367
* S C H _ c u r r e n t _ t h r e a d
369
**************************************
371
* Functional description
372
* Return id of current thread. If scheduling is not active,
375
**************************************/
377
return active_thread;
383
/**************************************
387
**************************************
389
* Functional description
390
* A thread wants to enter the access method and submit to our scheduling.
393
**************************************/
395
/* Special case single thread case */
399
THREAD thread = active_thread = free_threads;
401
thread->thread_next = thread->thread_prior = thread;
402
thread->thread_flags = 0;
403
thread->thread_id = ThreadData::getId();
412
/* Get mutex on scheduler data structures to prevent tragic misunderstandings */
414
sch_mutex_lock(thread_mutex);
416
THREAD thread = alloc_thread();
417
thread->thread_id = ThreadData::getId();
419
/* Link thread block into circular list of active threads */
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);
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;
435
thread->thread_next = thread->thread_prior = thread;
436
active_thread = thread;
438
ThreadPriorityScheduler::enter();
440
if (active_thread->thread_flags & THREAD_hiber) {
445
sch_mutex_unlock(thread_mutex);
451
/**************************************
455
**************************************
457
* Functional description
458
* Thread is done in access method, remove data structure from
459
* scheduler, and release thread block.
461
**************************************/
465
free_threads = active_thread;
466
active_thread = NULL;
467
free_threads->thread_next = NULL;
469
sch_mutex_lock(thread_mutex);
471
ast_enable(); /* Reenable AST delivery */
473
THREAD thread = active_thread;
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)
480
sch_mutex_unlock(thread_mutex);
484
if (thread == thread->thread_next)
485
active_thread = NULL;
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;
493
ThreadPriorityScheduler::exit();
495
thread->thread_next = free_threads;
496
free_threads = thread;
499
sch_mutex_unlock(thread_mutex);
506
/**************************************
510
**************************************
512
* Functional description
513
* Go to sleep until woken by another thread. See also
516
**************************************/
518
schedule_active(true);
522
static Firebird::Mutex scheduler_init_lock;
527
/**************************************
531
**************************************
533
* Functional description
534
* Initialize the thread scheduler.
536
**************************************/
541
scheduler_init_lock.enter();
546
gds__register_cleanup(cleanup, 0);
551
catch (const Firebird::Exception&) {
552
scheduler_init_lock.leave();
555
scheduler_init_lock.leave();
560
bool SCH_schedule(void)
562
/**************************************
564
* S C H _ s c h e d u l e
566
**************************************
568
* Functional description
569
* Voluntarily relinquish control so that others may run.
570
* If a context switch actually happened, return true.
572
**************************************/
573
return schedule_active(false);
577
bool SCH_thread_enter_check(void)
579
/**************************************
581
* S C H _ t h r e a d _ e n t e r _ c h e c k
583
**************************************
585
* Functional description
586
* Check if thread is active thread, if so return true
589
**************************************/
591
/* if active thread is not null and thread_id matches the we are the
593
sch_mutex_lock(thread_mutex);
594
const bool ret = ((active_thread) && (active_thread->thread_id == ThreadData::getId()));
595
sch_mutex_unlock(thread_mutex);
601
bool SCH_validate(void)
603
/**************************************
605
* S C H _ v a l i d a t e
607
**************************************
609
* Functional description
610
* Check integrity of thread system (assume thread is entered).
612
**************************************/
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"))
623
if (active_thread->thread_id != ThreadData::getId()) {
624
gds__log("SCH_validate -- wrong thread");
633
void SCH_wake(THREAD thread)
635
/**************************************
639
**************************************
641
* Functional description
642
* Take thread out of hibernation.
644
**************************************/
645
thread->thread_flags &= ~THREAD_hiber;
646
ISC_event_post(thread->thread_stall);
650
static THREAD alloc_thread(void)
652
/**************************************
654
* a l l o c _ t h r e a d
656
**************************************
658
* Functional description
659
* Allocate a thread block.
661
**************************************/
663
/* Find a useable thread block. If there isn't one, allocate one */
665
THREAD thread = free_threads;
667
free_threads = thread->thread_next;
669
thread = (THREAD) gds__alloc((SLONG) sizeof(struct thread));
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
677
gds_alloc_flag_unfreed((void *) thread);
679
ISC_event_init(thread->thread_stall, 0, 0);
682
thread->thread_flags = thread->thread_count = 0;
687
static bool ast_enable(void)
689
/**************************************
691
* a s t _ e n a b l e
693
**************************************
695
* Functional description
696
* Enables AST delivery and returns
697
* TRUE if an AST is deliverable.
699
**************************************/
703
if (ast_thread->thread_flags & THREAD_ast_active &&
704
ast_thread->thread_id == ThreadData::getId())
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);
722
static void ast_disable(void)
724
/**************************************
726
* a s t _ d i s a b l e
728
**************************************
730
* Functional description
731
* Disables AST delivery and waits
732
* until an active AST completes
735
**************************************/
739
if (ast_thread->thread_flags & THREAD_ast_active) {
740
if (ast_thread->thread_id == ThreadData::getId())
744
&& active_thread->thread_id == ThreadData::getId())
746
stall(active_thread);
750
THREAD thread = alloc_thread();
752
thread->thread_next = free_threads;
753
free_threads = thread;
758
ast_thread->thread_flags |= THREAD_ast_disabled;
759
++ast_thread->thread_count;
763
static void cleanup(void *arg)
765
/**************************************
769
**************************************
771
* Functional description
772
* Exit handler for image exit.
774
**************************************/
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) */
784
thread_mutex.enter();
789
/* loop through the list of active threads and free the events */
790
THREAD temp_thread = active_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 */
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
802
} while (temp_thread->thread_next != temp_thread
803
&& (temp_thread = temp_thread->thread_next));
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 */
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
819
} while (temp_thread->thread_next != temp_thread
820
&& (temp_thread = temp_thread->thread_next));
825
thread_mutex.leave();
826
#endif /* SUPERCLIENT */
832
static void mutex_bugcheck(const TEXT* string, int mutex_state)
834
/**************************************
836
* m u t e x _ b u g c h e c k
838
**************************************
840
* Functional description
841
* There has been a bugcheck during a mutex operation.
844
**************************************/
847
sprintf(msg, "SCH: %.93s error, status = %d", string, mutex_state);
849
fprintf(stderr, "%s\n", msg);
855
// CVC: Nobody checks the result from this function.
856
static bool schedule(void)
858
/**************************************
862
**************************************
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
869
**************************************/
873
THREAD thread = active_thread;
876
thread = thread->thread_next;
877
if (!(thread->thread_flags & THREAD_hiber))
879
if (thread == active_thread)
883
active_thread = thread;
884
ISC_event_post(active_thread->thread_stall);
890
static bool schedule_active(bool hiber_flag)
892
/**************************************
894
* s c h e d u l e _ a c t i v e
896
**************************************
898
* Functional description
899
* Voluntarily relinquish control so that others may run.
900
* If a context switch actually happened, return true.
902
**************************************/
909
sch_mutex_lock(thread_mutex);
911
/* Take this opportunity to check for pending ASTs
915
stall(active_thread);
920
active_thread->thread_flags |= THREAD_hiber;
921
THREAD thread = active_thread;
924
if (thread == active_thread && !(thread->thread_flags & THREAD_hiber))
932
sch_mutex_unlock(thread_mutex);
939
static void stall(THREAD thread)
941
/**************************************
945
**************************************
947
* Functional description
948
* Stall until our thread is made active.
950
**************************************/
951
if (thread != active_thread || thread->thread_flags & THREAD_hiber ||
952
(ast_thread && ast_thread->thread_flags & THREAD_ast_active))
955
SLONG value = ISC_event_clear(thread->thread_stall);
956
if (thread == active_thread
957
&& !(thread->thread_flags & THREAD_hiber)
959
|| !(ast_thread->thread_flags & THREAD_ast_active)))
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);
970
/* Explicitly disable AST delivery for active thread */
976
static void stall_ast(THREAD thread)
978
/**************************************
982
**************************************
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
990
**************************************/
991
if (thread == ast_thread) {
992
if (ast_thread->thread_flags & THREAD_ast_disabled)
994
SLONG value = ISC_event_clear(thread->thread_stall);
995
if (!(ast_thread->thread_flags & THREAD_ast_disabled))
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);
1004
/* Link thread block into ast thread queue */
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;
1011
/* Wait for AST delivery to complete */
1013
if (ast_thread->thread_flags & THREAD_ast_active)
1015
SLONG value = ISC_event_clear(thread->thread_stall);
1016
if (!(ast_thread->thread_flags & THREAD_ast_active))
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);
1023
/* Unlink thread block from ast thread queue */
1025
thread->thread_prior->thread_next = thread->thread_next;
1026
thread->thread_next->thread_prior = thread->thread_prior;
1031
static void sch_mutex_lock(Firebird::Mutex& mtx)
1033
/**************************************
1035
* s c h _ m u t e x _ l o c k
1037
**************************************
1039
* Functional description
1040
* Enters mutex, on error bugcheks.
1042
**************************************/
1047
catch (const Firebird::system_call_failed& e)
1049
mutex_bugcheck("mutex lock", e.getErrorCode());
1054
static void sch_mutex_unlock(Firebird::Mutex& mtx)
1056
/**************************************
1058
* s c h _ m u t e x _ u n l o c k
1060
**************************************
1062
* Functional description
1063
* Leaves mutex, on error bugcheks.
1065
**************************************/
1070
catch (const Firebird::system_call_failed& e)
1072
mutex_bugcheck("mutex unlock", e.getErrorCode());