-
Notifications
You must be signed in to change notification settings - Fork 0
/
rt.cpp
executable file
·1811 lines (1445 loc) · 71.1 KB
/
rt.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
///////////////////////////////////////////////////////////////////////////////////////////////
// Notes for using this code on Microsofts Visual C++ V6.0
//
// If you have trouble linking the program, with errors relating to
// _beginthreadex() undefined or similar, then see the project settings options in your handout
// on creating concurrent software using Visual C++
///////////////////////////////////////////////////////////////////////////////////////////////
//#include <StdAfx.h> // delete this line if you are NOT using Microsoft MFC in your project
// for example, delete this line if you are creating a Console project
// but if you are creating a GUI or window type application, leave it in
// NOTE You will have to change the pathlist to the 'rt.h' header file below to indicate
// to the compiler where it can find this file. This will of course depend upon where it resides
// on your computer i.e. where you copied it to.
#include "rt.h"
// constructor to create a child process, takes four
//arguments, note that the last 3 make use
// of default argument, that is, if you do not supply
//real arguments in the call, then the default values will
// be used for the ones you do NOT supply
// this means that wehn you create a new CProcess object,
//the only argument you HAVE to supply is the Name of the
//executable
// all other values can be supplied by default
//##ModelId=3DE6123A0037
CProcess::CProcess(
const string &Name, // path/name of executable program (.exe)
int Priority, // Priority of the process
BOOL bUseNewWindow, // use OWN_WINDOW to make new process run in its own window
// use PARENT_WINDOW to make it share its parent window
BOOL bCreateSuspended, // use SUSPENDED to make new child process's main thread suspended when process is created
// use ACTIVE to make new child processes main thread active when process iscreated
const string &ChildProcessArgString)
: ProcessName(Name)
{
// Check priority level is valid
PERR(( (Priority == HIGH_PRIORITY_CLASS) ||
(Priority == IDLE_PRIORITY_CLASS) ||
(Priority == NORMAL_PRIORITY_CLASS) ||
(Priority == REALTIME_PRIORITY_CLASS)) ,
string("Illegal 2nd Argument (Process Priority) for Process: ") + Name) ;
PERR ((bUseNewWindow == OWN_WINDOW || bUseNewWindow == PARENT_WINDOW),
string("Use OWN_WINDOW or PARENT_WINDOW as 3rd argument for Process: ") + Name ) ;
PERR ((bCreateSuspended == SUSPENDED || bCreateSuspended == ACTIVE),
string("Use SUSPENDED or ACTIVE as 4th Argument for Process: ") + Name) ;
STARTUPINFO StartupInfo = {
sizeof(STARTUPINFO) ,
NULL , // reserved
NULL , // ignored in console applications
(char *)(Name.c_str()) , // displayed in title bar for console applications
0,0, // dwx, dwy, offset of top left of new window relative to top left of screen in pixel
// flags below must specify STARTF_USEPOSITION. Ignored for console apps'
0,0, // dwxsize, dwysize: Width and height of the window if new window specified
// must use flags STARTF_USESIZE. Ignored for console apps'
0,0, // size of console in characters, only if STARTF_USECOUNTCHARS flag specified,
// Ignored for console apps
0, // Colour control, for background and text. Ignored for console apps
0, // Flags. Ignored for console applications
0, // ignored unless showwindow flag set
0 ,
NULL,
0,0,0 // stdin, stdout and stderr handles (inherited from parent)
} ;
UINT flags = Priority ; // Priority,
if(bUseNewWindow == OWN_WINDOW) // if caller has specified that child should have its won window
flags |= CREATE_NEW_CONSOLE ;
if(bCreateSuspended == SUSPENDED) // if caller has specified that child process should be immediately suspended
flags |= CREATE_SUSPENDED ;
BOOL Success = CreateProcess( NULL, // application name
(char *)(Name.c_str()), // Command line to the process if you want to pass one to main() in the process
NULL, // process attributes
NULL, // thread attributes
TRUE, // inherits handles of parent
flags, // Priority and Window control flags,
NULL, // use environent of parent
NULL, // use same drive and directory as parent
&StartupInfo , // controls appearance of process (see above)
&pInfo // Stored process handle and ID into this object
);
PERR( Success == TRUE, string("CProcess Call Unable to Create New Process: ") + Name) ; // check for error and print message if appropriate
}
////////////////////////////////////////////////////////////////////////////////////////////
// The following function can be used to change the 'base' priority of a process once it has been
// created (irrespective of whether it is active or suspended. Processes with higher priority
// will block/stop processes with lower priority from executing so be careful
//
// All you need is the handle for the process obtained from FORK_PROCESS() (see above)
// Windows NT has a limited number of thread priorities and the following symbolic
// constants can be used as a valid value for the parameter 'Priority' in the function
// Normally a thread will inherit a 'base' priority from its parent thread, or, if it is
// the primary thread, from its parent process (See FORK_PROCESS() above
// The constant below simply allow you to make small adjustments to these value
/////////////////////////////////////////////////////////////////////////////////////////////
//
// HIGH_PRIORITY_CLASS Specify this class for a process that performs time-critical tasks that must be executed immediately. The threads of the process preempt the threads of normal or idle priority class processes. An example is Windows Task List, which must respond quickly when called by the user, regardless of the load on the operating system. Use extreme care when using the high-priority class, because a high-priority class application can use nearly all available CPU time.
// IDLE_PRIORITY_CLASS Specify this class for a process whose threads run only when the system is idle. The threads of the process are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle-priority class is inherited by child processes.
// NORMAL_PRIORITY_CLASS Specify this class for a process with no special scheduling needs.
// REALTIME_PRIORITY_CLASS Specify this class for a process that has the highest possible priority. The threads of the process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.
//
// Returns TRUE on Success/False on failure
//
//##ModelId=3DE6123A009C
BOOL CProcess::SetPriority(int Priority) const
{
BOOL Success;
// Check priority level
PERR(((Priority == HIGH_PRIORITY_CLASS) || (Priority == IDLE_PRIORITY_CLASS) ||
(Priority == NORMAL_PRIORITY_CLASS) || (Priority == REALTIME_PRIORITY_CLASS)) ,
string("Illegal Priority value in call to SetPiority()")) ;
Success = SetPriorityClass(GetProcessHandle(), Priority) ;
PERR( Success == TRUE, string("Unable to Set Thread Priority of Process: ") + ProcessName) ; // check for error and print error message as appropriate
return Success ;
}
//
// These two functions can be called to suspend and resume a processes activity.
// Actually, it suspend/resumes the process primary thread and not necessarily any
// other child threads it may have. Once suspended the thread will no longer
// run and consume CPU time. Note that if you suspend a process more than once,
// then it will require the appropriate number of resumes to allow it to continue
//
//
// Returns TRUE on Success, FALSE on Failure
//
//##ModelId=3DE6123A007F
BOOL CProcess::Suspend() const
{
UINT Result = SuspendThread(GetThreadHandle()) ;
PERR( Result != 0xffffffff, string("Cannot Suspend Process: ") + ProcessName) ;
if(Result != 0xffffffff) // if no error
return TRUE ;
else
return FALSE ;
}
//##ModelId=3DE6123A0088
BOOL CProcess::Resume() const
{
UINT Result = ResumeThread(GetThreadHandle()) ;
PERR( Result != 0xffffffff, string("Cannot Resume Process: ") + ProcessName) ;
if(Result != 0xffffffff) // if no error
return TRUE ;
else
return FALSE ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This function waits for a child process to terminate provide it has been
// created as a CProcess Object. Or returns if the specified time elapses before the process has terminates
//
// Note that if the child process has already terminated, when the call to wait for
// it is made, then this is deemed a success, NOT a failure and the function returns immediately without waiting
//
// This function returns WAIT_FAILED if there was an error, WAIT_TIMEOUT if the wait operation timed out
// of WAIT_OBJECT_0 if the operation did in fact wait and then returned
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//##ModelId=3DE6123A0092
BOOL CProcess::WaitForProcess(DWORD Time) const
{
UINT Result = WaitForSingleObject(GetProcessHandle(), Time) ;
PERR( Result != WAIT_FAILED, string("Cannot Wait for Child Process: ") + ProcessName + string(" to Terminate.\n It might already be dead")) ; // check for error and print message if appropriate== WAIT_OBJECT_0 )
return Result ;
}
// The following function sends a message to a process' main thread
// If the thread is currently suspended, waiting for a message, then posting it a message
// will wake it up and allow it to continue
//
// The Message must be in the range 0 - 32000
// In order to receive messages, the thread receiving the message must have created a message queue.
// (See CMessageQueue later)
//
//##ModelId=3DE6123A00AF
BOOL CProcess::Post(UINT Message) const // message value and ID of thread
{
BOOL Result ;
if(Message > 32000) {
PERR( Message <= 32000, string("Could not Signal Process:") + ProcessName + string("\nReason: Message Value > 32000")) ; // check for error and print error message as appropriate
return FALSE ;
}
else {
Result = PostThreadMessage(GetThreadId(), WM_USER + Message, 0, 0L) ;
PERR( Result != 0, string("Could not Signal Process:") + ProcessName + string("\nProcess May be Dead")) ; // check for error and print error message as appropriate
return Result ;
}
}
//
// This calls standard exit() function to terminate the process
//
//##ModelId=3DE6123A00BA
void CProcess::Exit(UINT ExitCode) const
{
exit(ExitCode) ;
}
// This function create a parallel thread within a process (do not confuse this
// with creating a new process. Each and every process (i.e. program/application)
// can have many threads. At startup, a process will have just 1 thread which commences
// with the execution of main() in your 'C' program. Once started, this 'primary' thread
// can create secondary threads which run in parallel with each other AND the main thread
//
// A thread is simply a 'C' style function (see examples below) defined in a special way.
// A thread inherits the priority of its parent thread, but can be changed later
//
// A Thread function MUST be declared in a prescribed style (see example below).
// A process can create/maintain many such threads and they can share global variables
// thus implementing thread-to-thread communication in a simple and effective way
// and even call the same functions.
//##ModelId=3DE6123A0182
CThread::CThread(UINT __stdcall Function( void *), // name/pointer to function that is to be the new thread
BOOL bCreateState, // A flag indicating if the thread should commence SUSPENDED (TRUE) or ACTIVE (FALSE)
void *ThreadArgs // a generic pointer (can point to anything) to any data the calling thread
// wishes to pass to the child thread
)
{
UINT ThreadControlFlags = 0 ;
if(bCreateState == SUSPENDED) // if caller wants thread initially suspended
ThreadControlFlags = CREATE_SUSPENDED ;
ThreadHandle = (HANDLE)(_beginthreadex(NULL, 0, Function, ThreadArgs, ThreadControlFlags, &ThreadID)) ;
PERR( ThreadHandle != 0, string("Unable to Create Thread")) ; // check for error and print message if appropriate
}
/*
** The following two functions work together to assist in the design of CThread derived classes with a main() to simulate the main of the thread
** All active class objects have their root thread within this function
*/
UINT __stdcall __GlobalThreadMain__(void *theThreadPtr) // receives a pointer to the thread object
{
ExitThread(((ActiveClass *)(theThreadPtr))->main()) ; // run the activeclass virtual main function it should be overridden in derived class
return 0 ;
}
//##ModelId=3DE6123A0178
CThread::CThread(BOOL bCreateState) // A flag indicating if the thread should commence SUSPENDED (TRUE) or ACTIVE (FALSE)
{
UINT ThreadControlFlags = 0 ;
if(bCreateState == SUSPENDED) // if caller wants thread initially suspended
ThreadControlFlags = CREATE_SUSPENDED ;
ThreadHandle = (HANDLE)(_beginthreadex(NULL,
0,
__GlobalThreadMain__,
this, //pointer to cthread derived object passed to __GlobalThreadMain__() above
ThreadControlFlags, // thread is created in suspended state
&ThreadID)) ;
PERR( ThreadHandle != 0, string("Unable to Create Thread")) ; // check for error and print message if appropriate
}
//##ModelId=3DE6123A0223
ActiveClass::ActiveClass()
: TerminateFlag(FALSE) ,
CThread() // Call base class constructor to create the SUSPENDED thread, i.e. main in derived class does not yet run
// All objects derived from active class are thus initially created in the suspended state and have to
// be RESUMED before they become active. This is the safest way, as it allows control over the
// order in which objects run and avoids potential race hazards.
{} // Note that active class objects like all derived classes are constructed from the base upwards
// and thus we have to make sure that base/derived class constructors are run to completion
// before the thread for the class (which is created in the base class) is allowed to resume
// otherwise the thread could run BEFORE the active class object had completed its construction
// this was the main reason for creating all ActiveClass objects in the Suspended state so don't change it
// I learn't the hard way and it was painful to debug !!!
// this is important as we want to allow the derived
//classes constructor i.e. the one
// written by the user, to complete its constructor
//before the class main() is run
// otherwise the class might run with unitialised
//variables/state
// This means that the user has to call Resume() for the
//class object to allow it to run.
//##ModelId=3DE6123A022C
ActiveClass::~ActiveClass()
{}
//
// The following can be used to terminate a thread at any point in the thread execution
// If the thread reaches the end of its 'thread function' then executing return 0 does the same trick.
//
//##ModelId=3DE6123A01D4
void CThread::Exit(UINT ExitCode) const
{
ExitThread(ExitCode) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////
// These two functions can be called to suspend and resume a threads activity. Once suspended
// the thread will no longer run and consume CPU time
//
// Note that if you suspend a thread more than once, then it will require that many number
// of resumes to allow it to continue
//
// Both functions require the thread HANDLE, which can be obtained when the thread is created
///////////////////////////////////////////////////////////////////////////////////////////////
//##ModelId=3DE6123A01AA
BOOL CThread::Suspend() const
{
UINT Result = SuspendThread(ThreadHandle) ;
PERR( Result != 0xffffffff, string("Cannot Suspend Thread\n")) ; // check for error and print message if appropriate
if(Result != 0xffffffff)
return TRUE ;
else
return FALSE ;
}
//##ModelId=3DE6123A01B4
BOOL CThread::Resume() const
{
UINT Result = ResumeThread(ThreadHandle) ;
PERR( Result != 0xffffffff, string("Cannot Resume Thread\n")) ; // check for error and print message if appropriate
if(Result != 0xffffffff)
return TRUE ;
else
return FALSE ;
}
////////////////////////////////////////////////////////////////////////////////////////////
// The following function can be used to change the priority of a thread once it has been
// created (irrespective of whether it is active or suspended). Threads with higher priority
// will block/stop threads with lower priority from executing so be careful.
//
// All you need is the handle for the thread obtained from CREATE_THREAD() (see above)
// Windows NT has a limited number of thread priorities and the following symbolic
// constants can be used as a valid value for the parameter 'Priority' in the function
// Normally a thread will inherit a 'base' priority from its parent thread, or, if it is
// the primary thread, from its parent process (See FORK_PROCESS() above
// The constant below simply allow you to make small adjustments to the thread priority
/////////////////////////////////////////////////////////////////////////////////////////////
//
// THREAD_PRIORITY_ABOVE_NORMAL = Indicates 1 point above normal priority for the priority class.
// THREAD_PRIORITY_BELOW_NORMAL = Indicates 1 point below normal priority for the priority class.
// THREAD_PRIORITY_HIGHEST = Indicates 2 points above normal priority for the priority class.
// THREAD_PRIORITY_IDLE = Indicates a base priority level of 1 for IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and a base priority level of 16 for REALTIME_PRIORITY_CLASS processes.
// THREAD_PRIORITY_LOWEST = Indicates 2 points below normal priority for the priority class.
// THREAD_PRIORITY_NORMAL = Indicates normal priority for the priority class.
// THREAD_PRIORITY_TIME_CRITICAL = Indicates a base priority level of 15 for IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, or HIGH_PRIORITY_CLASS processes, and a base priority level of 31 for REALTIME_PRIORITY_CLASS processes.
/////////////////////////////////////////////////////////////////////////////////////////////
//##ModelId=3DE6123A01BF
BOOL CThread::SetPriority(UINT Priority) const
{
BOOL Success;
// check for error in priority value and print message if appropriate
PERR(((Priority == THREAD_PRIORITY_ABOVE_NORMAL) ||
(Priority == THREAD_PRIORITY_BELOW_NORMAL) ||
(Priority == THREAD_PRIORITY_HIGHEST) ||
(Priority == THREAD_PRIORITY_IDLE) ||
(Priority == THREAD_PRIORITY_LOWEST) ||
(Priority == THREAD_PRIORITY_NORMAL) ||
(Priority == THREAD_PRIORITY_TIME_CRITICAL)) ,
string("Illegal Priority value specified for Thread in call to CThread::SetPriority()")) ;
Success = SetThreadPriority(ThreadHandle, Priority) ; // set priority
PERR( Success == TRUE, string("Cannot Set Thread Priority\n")) ; // check for error and print message if appropriate
return Success ;
}
//
// This function will wait for the child thread specified by ThreadHandle to terminate
// main or parent threads can use this to wait for their child thread to terminate
//
// A time period can be specified indicating the maximum time to attempt a wait
//
// This function returns WAIT_FAILED if there was an error, WAIT_TIMEOUT if the wait operation timed out
// of WAIT_OBJECT_0 if the operation did in fact wait and then returned
//
//##ModelId=3DE6123A01B6
UINT CThread::WaitForThread(DWORD Time) const
{
UINT Result = WaitForSingleObject(ThreadHandle, Time) ; // return WAIT_FAILED on error
PERR( Result != WAIT_FAILED, string("Cannot Wait For Thread")) ; // check for error and print error message as appropriate
return Result ;
}
// The following function sends a message to a thread
// If the thread is currently suspended, waiting for a message, then posting it a message
// will wake it up and allow it to continue
//
// Message must bve in the range 0 - 32000
// In order to use messages, the thread receiving the message must have created a message queue.
// (See CMessageQueue later )
//
//##ModelId=3DE6123A01CA
BOOL CThread::Post(UINT Message) const // message value and ID of thread
{
BOOL Result ;
if(Message > 32000) {
PERR( Message <= 32000, string("Could not Post User Message: Message > 32000")) ; // check for error and print error message as appropriate
return FALSE ;
}
else {
Result = PostThreadMessage(ThreadID, WM_USER + Message, 0, 0L) ;
PERR( Result != 0, string("Could not Post User Message. The Thread might have died")) ; // check for error and print error message as appropriate
return Result ;
}
}
// constructs mutex with name and indicates whether
// object protected by mutex is owned by creator or not
// Note use of default argument, i.e. if you do not
// specifiy a value for bOwned argument
// then it will default to NOTOWNED
//##ModelId=3DE6123A0397
CMutex::CMutex(const string &Name, BOOL bOwned) // needs a name for the mutex (i.e. a string) and a flag
// indicating if the mutex is owned by the process that created it (Use OWNED or NOTOWNED for this value)
:MutexName(Name)
{
if(bOwned == OWNED) bOwned = TRUE ;
else bOwned = FALSE ;
MutexHandle = CreateMutex(NULL, bOwned, (char *)(Name.c_str())) ;
PERR( MutexHandle != NULL, string("Cannot Create Mutex: ") + Name) ; // check for error and print message if appropriate
}
//////////////////////////////////////////////////////////////////////
// unlinks a thread from a Mutex, provided you have its handle
// Returns TRUE/FALSE on SUCCESS/FAIL
// when all threads have unlinked from a mutex it will be destroyed automatically
//////////////////////////////////////////////////////////////////////
//##ModelId=3DE6123A0383
BOOL CMutex::Unlink() const
{
BOOL Success = CloseHandle(MutexHandle) ;
PERR( Success == TRUE, string("Cannot Unlink from Mutex:") + MutexName) ; // check for error and print message if appropriate
return Success ;
}
//
// This function will attempt to perform a WAIT on a Mutex. If the Mutex has been signalled
// (new mutexs are created in the 'signalled' or free state), then the process will be allowed to
// continue and the mutex will be set to the non-signalled state. This would mean
// that any other process coming along and performing a wait would be blocked until the mutex
// is signalled once again.
//
// This function returns WAIT_FAILED if there was an error, WAIT_TIMEOUT if the wait operation timed out
// of WAIT_OBJECT_0 if the operation did in fact wait and then returned
//
//##ModelId=3DE6123A036D
UINT CMutex::Wait(DWORD Time) const // return an unsigned int or UINT
{
UINT Result = WaitForSingleObject(MutexHandle, Time) ; // returns WAIT_FAILED on error
PERR( Result != WAIT_FAILED, string("Cannot Perfom WAIT operation on Mutex: ") + MutexName) ; // check for error and print message if appropriate
return Result ;
}
//
// This function will attempt to 'signal' a Mutex. This will allow one blocked
// process to proceed (setting the mutex back to the non-signalled state), otherwise
// if no processes are waiting/blocked, the mutex remains in the signalled state
//
//##ModelId=3DE6123A0377
BOOL CMutex::Signal() const
{
BOOL Success = ReleaseMutex( MutexHandle) ; // FALSE on failure, TRUE on success
PERR( Success == TRUE, string("Cannot Perfom SIGNAL operation on Mutex: ") + MutexName) ; // check for error and print message if appropriate
return Success ;
}
//
// This function reads the value of a Mutex, without signalling or waiting on it
// it can be used to poll the value of the Mutex to perhaps determine if it is safe to
// perform a wait without getting suspended/blocked
// returns WAIT_FAILED if there was an error.
//
// If the Mutex has been signalled, then this function returns TRUE
// if the Mutex has NOT been signalled, then this function returns FALSE
//
//##ModelId=3DE6123A0381
BOOL CMutex::Read() const // Handle of the Mutex needed
{ // returns true/false state of Mutex
BOOL Signalled ;
// first wait for the object. Timeout is zero, so function
// should immediately wait and decrement the Mutex value if it is signalled (i.e. >1)
Signalled = WaitForSingleObject(MutexHandle, 0) ; // see of Mutex is signalled or not
PERR( Signalled != WAIT_FAILED, string("Cannot Perfom READ operation on Mutex: ") + MutexName) ; // check for error and print message if appropriate
// Now if we did a wait and managed to decrement the Mutex, then
// the above function call should have returned WAIT_OBJECT_0, thus the process
// of attempting to read the Mutex has unfortunately resulted in its value
// being decremented, so we have to put that write by signalling it again. This should be
// safe, since we cannot exceed the limits of the Mutex max value as we are
// only putting it back to what it was before we attempted this
if(Signalled == WAIT_FAILED)
return WAIT_FAILED ;
if(Signalled == WAIT_OBJECT_0) {
BOOL Success = ReleaseMutex(MutexHandle) ; // signal mutex if we decremented it
PERR( Success == TRUE, string("Cannot Perfom READ operation on Mutex: ") + MutexName) ; // check for error and print message if appropriate
return (UINT)TRUE ;
}
else
return (UINT)FALSE ; // non-signalled
}
////////////////////////////////////////////////////////////
// ReaderWriters Mutex Problem
////////////////////////////////////////////////////////////
//
// This version gives preference to readers rather than writers
//
CReadersWritersMutex::CReadersWritersMutex(const string &MyName) : Name(MyName)
{
ReadersWritersMutex = new CMutex(string("__CReadersWritersMutex__") + MyName) ;
ReadersWritersSemaphore = new CSemaphore(string("__CReadersWritersSemaphore__") + MyName, 1, 1) ;
ReadersWritersDataPool = new CDataPool(string("__CReadersWritersDataPool__") + MyName, sizeof(struct Data)) ;
ptr = (struct Data *)(ReadersWritersDataPool->LinkDataPool()) ;
if(strcmp(ptr->Initialised, "Initialised") != 0) { // if pool not initialised
ptr->NumberOfReaders = 0 ;
strcpy_s(ptr->Initialised, "Initialised"); // show me as initialised
}
}
CReadersWritersMutex::~CReadersWritersMutex()
{
delete ReadersWritersMutex;
delete ReadersWritersSemaphore;
strcpy_s(ptr->Initialised, "") ;
delete ReadersWritersDataPool;
}
//
// called by a reader when they wish to access to the resource
//
void CReadersWritersMutex::WaitToRead()
{
ReadersWritersMutex->Wait(); // wait for exclusive access
// increment the number of waiting readers
if(++(ptr->NumberOfReaders) == 1) // if this is the 1st reader
ReadersWritersSemaphore->Wait(); // wait on the semaphore as a writer may be using the resource
ReadersWritersMutex->Signal() ; // release access to resource
}
//
// called by a reader when they have finished with the resource
//
void CReadersWritersMutex::DoneReading()
{
ReadersWritersMutex->Wait(); // wait for exclusive access
// decrement the number of waiting readers
if(--(ptr->NumberOfReaders) == 0) // if no readers
ReadersWritersSemaphore->Signal() ; // signal that writers may now enter
ReadersWritersMutex->Signal() ; // release access to resource
}
//
// called by a writer when they wish to access to the resource
//
void CReadersWritersMutex::WaitToWrite()
{
ReadersWritersSemaphore->Wait() ; // used to exclude all readers
}
//
// called by a writer when they have finished with the resource
//
void CReadersWritersMutex::DoneWriting()
{
PERR((ptr->NumberOfReaders < 2) , "HELP");
ReadersWritersSemaphore->Signal() ; // allow readers in
}
////////////////////////////////////////////////////////////
// WritersReader Mutex Problem
////////////////////////////////////////////////////////////
//
// This version gives preference to writers rather than readers
//
CWritersReadersMutex::CWritersReadersMutex(const string &MyName) : Name(MyName)
{
WritersReadersMutex = new CMutex(string("__CWritersReadersMutex__") + MyName) ;
WritersReadersSemaphore = new CSemaphore(string("__CWritersReadersSemaphore__") + MyName, 1, 1) ;
WritersReadersCondition = new CCondition(string("__CWritersReadersCondition__") + MyName, MANUAL, SIGNALLED) ;
WritersReadersDataPool = new CDataPool(string("__CWritersReadersDataPool__") + MyName, sizeof(struct Data)) ;
ptr = (struct Data *)(WritersReadersDataPool->LinkDataPool()) ;
if(strcmp(ptr->Initialised, "Initialised") != 0) { // if pool not initialised
ptr->NumberOfReaders = 0 ;
ptr->NumberOfWriters = 0 ;
strcpy_s(ptr->Initialised, "Initialised"); // show me as initialised
}
}
CWritersReadersMutex::~CWritersReadersMutex()
{
delete WritersReadersMutex;
delete WritersReadersSemaphore;
delete WritersReadersCondition ;
strcpy_s(ptr->Initialised, "") ;
delete WritersReadersDataPool;
}
//
// called by a reader when they wish to access to the resource
//
void CWritersReadersMutex::WaitToRead()
{
WritersReadersCondition->Wait() ; // wait until no writers
WritersReadersMutex->Wait(); // wait for exclusive access
// increment the number of waiting readers
if(++(ptr->NumberOfReaders) == 1) // if this is the 1st reader
WritersReadersSemaphore->Wait(); // wait on the semaphore as a writer may be waiting to use the resource
WritersReadersMutex->Signal() ; // release access to resource
}
//
// called by a reader when they have finished with the resource
//
void CWritersReadersMutex::DoneReading()
{
WritersReadersMutex->Wait(); // wait for exclusive access
if(--(ptr->NumberOfReaders) == 0) // if no readers
WritersReadersSemaphore->Signal() ; // signal last reader has left and that any waiting writers may now enter
WritersReadersMutex->Signal() ; // release access to resource
}
//
// called by a writer when they wish to access to the resource
//
void CWritersReadersMutex::WaitToWrite()
{
WritersReadersMutex->Wait(); // wait for exclusive access to data
if(++(ptr->NumberOfWriters) == 1 ) // if I am the only writer waiting
WritersReadersCondition->Reset() ; // block future readers until this writer has gained access and has released it
WritersReadersMutex->Signal() ; // release access to data
WritersReadersSemaphore->Wait() ; // wait until all other process have left resource (readers or writers)
}
//
// called by a writer when they have finished with the resource
//
void CWritersReadersMutex::DoneWriting()
{
WritersReadersSemaphore->Signal() ; // allow next writer or reader in depending on who is first in line
WritersReadersMutex->Wait(); // wait for exclusive access to data
if(--(ptr->NumberOfWriters) == 0 ) // if there are no more waiting writers
WritersReadersCondition->Signal() ; // remove the condition blocking entry by readers
WritersReadersMutex->Signal() ; // release access to data
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CRendezvous Class
///////////////////////////////////////////////////////////////////////////////////////////////////////////
CRendezvous::CRendezvous(const string &TheRendezvousName, int NumberThreads) : RendezvousName(TheRendezvousName)
{
RendezvousMutex = new CMutex(string("__RendezvousMutex__") + RendezvousName) ;
RendezvousEvent = new CEvent(string("__RendezvousEvent__") + RendezvousName) ;
RendezvousDataPool = new CDataPool(string("__RendezvousDataPool__") + RendezvousName, sizeof(struct RendezvousData)) ;
ptr = (struct RendezvousData *)(RendezvousDataPool->LinkDataPool()) ;
RendezvousMutex->Wait() ;
if(strcmp(ptr->Initialised, "Initialised") != 0) // if not initialised
{
strcpy_s(ptr->Initialised, "Initialised") ; // initialise it
ptr->NumberWaiting = NumberThreads ; // set number of threads waiting to rendezvous
ptr->NumThreads = NumberThreads ;
}
else // check the number is the same as previous initialisation of this rendezvous
PERR(NumberThreads == ptr->NumThreads, string("Rendezvous '") + RendezvousName + string("' Already Created with a Different Number of Clients")) ;
RendezvousMutex->Signal() ;
}
void CRendezvous::Wait() {
RendezvousMutex->Wait() ;
if(--(ptr->NumberWaiting) == 0)
{
ptr->NumberWaiting = ptr->NumThreads ; // reset the count
RendezvousEvent->Signal() ; // release all waiting threads
RendezvousMutex->Signal() ;
}
else
{
RendezvousMutex->Signal() ;
RendezvousEvent->Wait() ;
}
}
CRendezvous::~CRendezvous()
{
strcpy_s(ptr->Initialised, "") ;
delete RendezvousDataPool ;
delete RendezvousMutex ;
delete RendezvousEvent ;
}
////////////////////////////////////////////////////////////
// Event Functions
////////////////////////////////////////////////////////////
CEvent::CEvent(const string &Name, BOOL bType, BOOL bState) // btype = SINGLE_RELEASE or MULTIPLE_RELEASE to allow one or many thread to resume when event is signalled
{ // bState = SIGNALLED or NOTSIGNALLED to indicate the initial or creation state of the event
PERR(bState == SIGNALLED || bState == NOTSIGNALLED, string("Illegal Signalled/NotSignalled Type specified when creating CEvent: ") + EventName) ;
if(bState == SIGNALLED)
bState = TRUE ;
else
bState = FALSE ;
PERR(bType == SINGLE_RELEASE || bType == MULTIPLE_RELEASE, string("Illegal Single or Multithread Type specified when creating CEvent: ") + EventName) ;
if(bType == SINGLE_RELEASE)
bType = FALSE ; // Win32 Auto-reset event
else
bType = TRUE ; // Win32 manual-reset event
EventHandle = CreateEvent(NULL, bType, bState, (char *)(Name.c_str())) ;
PERR( EventHandle != NULL, string("Cannot Create CEvent: ") + Name) ; // check for error and print message if appropriate
}
BOOL CEvent::Unlink() const { // unlink from event, i.e. we have finished using it
BOOL Success = CloseHandle(EventHandle) ;
PERR(Success != 0, string("Cannot Unlink the CEvent: ") + EventName) ; // check for error and print message if appropriate
return Success ;
}
// Signal() sets the event and will release ONLY ONE Waiting thread. It is then automatically reset.
// If no thread is waiting at the time it is signalled, then it is STILL RESET immediately
BOOL CEvent::Signal() const
{
BOOL Success = PulseEvent(EventHandle) ;
PERR( Success != 0, string("Cannot SignalAndReset() the CEvent: ") + EventName) ; // check for error and print message if appropriate
return Success ;
}
UINT CEvent::Wait(DWORD Time) const // perform a wait on an event for ever or until specified time
{
UINT Status = WaitForSingleObject(EventHandle, Time) ;
PERR(Status != WAIT_FAILED, string("Cannot Wait for CEvent: ") + EventName) ; // check for error and print message if appropriate
return Status ;
}
////////////////////////////////////////////////////////////
// Condition Functions
////////////////////////////////////////////////////////////
CCondition::CCondition(const string &Name, BOOL bType, BOOL bState)
{
PERR(bState == SIGNALLED || bState == NOTSIGNALLED, string("Illegal Signalled/NotSignalled Type specified when creating CCondition: ") + ConditionName) ;
if(bState == SIGNALLED) bState = TRUE ;
else bState = FALSE ;
PERR(bType == MANUAL || bType == AUTORESET, string("Illegal Signalled/NotSignalled Type specified when creating CCondition: ") + ConditionName) ;
if(bType == MANUAL) bType = TRUE ;
else bType = FALSE ;
ConditionHandle = CreateEvent(NULL, bType, bState, (char *)(Name.c_str())) ; // based around a Win32 manual event
PERR( ConditionHandle != NULL, string("Cannot Create CCondition: ") + Name) ; // check for error and print message if appropriate
}
BOOL CCondition::Unlink() const { // unlink from Condition, i.e. we have finished using it
BOOL Success = CloseHandle(ConditionHandle) ;
PERR(Success != 0, string("Cannot Unlink the CCondition: ") + ConditionName) ; // check for error and print message if appropriate
return Success ;
}
// Signal() sets the Condition and will release ALL Waiting threads. It can be reset by calling Reset()
BOOL CCondition::Signal() const {
BOOL Success = SetEvent(ConditionHandle) ;
PERR( Success != 0, string("Cannot Set() the CCondition: ") + ConditionName) ; // check for error and print message if appropriate
return Success ;
}
UINT CCondition::Wait(DWORD Time) const // perform a wait on a Condition for ever or until specified time
{
UINT Status = WaitForSingleObject(ConditionHandle, Time) ;
PERR(Status != WAIT_FAILED, string("Cannot Wait for CCondition: ") + ConditionName) ; // check for error and print message if appropriate
return Status ;
}
BOOL CCondition::Reset() const // reset the condition back to false or not signalled
{
BOOL Success = ResetEvent(ConditionHandle) ;
PERR( Success != 0, string("Cannot Reset CCondition: ") + ConditionName) ; // check for error and print message if appropriate
return Success ;
}
BOOL CCondition::Test() const // see if condition is signalled
{
UINT Status = WaitForSingleObject(ConditionHandle, 0) ;
PERR( Status != WAIT_FAILED, string("Cannot Test Value of CAutoResetCondition: ") + ConditionName) ; // check for error and print message if appropriate
if(Status == WAIT_FAILED)
return WAIT_FAILED ;
else if(Status == WAIT_OBJECT_0) // if event signalled
return TRUE ; // return TRUE
else
return FALSE ; // esle return FALSE
}
////////////////////////////////////////////////////////////
// Semaphore Functions
////////////////////////////////////////////////////////////
//
// Semaphore's are used primarily by processes that need to make sure that a limited number of
// processes are allowed to access a resource at any one time.
//
// In order to use a Semaphore, create one using the function below, giving it a name
// if the Semaphore is new, i.e. there is not already a Semaphore with that name in existence,
// then a new one will be created using the initial value specified and limiting its maximum value to that specified
// However, if one already exists, then the function below simply links to it
// or creates a reference to it. (the max and initial values will in this instance be ignored)
//
// In essence, all processes that wish to use the Semaphore attempt to create it.
// Only one process will actually create it, the others simply reference or share the Semaphore
// with the same name. In either case, the function returns a HANDLE to the object.
// This handle can be used in subsequent calls relating to the use of Semaphores. In the event
// that an error occurs, the returned value of the Handle will be NULL. the calling process
// can check this and take appropriate action
//
// An alternative is to use the LINK_SEMAPHORE() function below if you only want to
// use the Semaphore and not create it
//
// For a Binary semaphore specify Max value as 1
// construct semaphore by name with initial and max
//permissible values
//##ModelId=3DE6123B02A6
CSemaphore::CSemaphore(const string &Name, int InitialVal, int MaxVal) // name, starting value and Maximum value needed
:SemaphoreName(Name)
{
SemaphoreHandle = CreateSemaphore(0, InitialVal, MaxVal, (char *)(Name.c_str())) ;
PERR( SemaphoreHandle != NULL, string("Cannot Create Semaphore: ") + Name) ; // check for error and print message if appropriate
}
//////////////////////////////////////////////////////////////////////
// unlinks a Semaphore, provided you have its handle
// Returns TRUE/FALSE on SUCCESS/FAIL
//
// A sempahore is removed when all threads have unlinked from it
//////////////////////////////////////////////////////////////////////
//##ModelId=3DE6123B0293
BOOL CSemaphore::Unlink() const // Handle of the semaphore needed
{ // return TRUE/FALSE on Success/Failure
BOOL Success = CloseHandle(SemaphoreHandle) ;
PERR( Success == TRUE, string("Cannot Unlink from Semaphore: ") + SemaphoreName ) ; // check for error and print message if appropriate
return Success ;
}
//
// This function will attempt to perform a wait on a sempahore. If the sempahore has been signalled
// (new sempahores are NOT necessarily created in the 'signalled' state), then the process will be allowed to
// continue and the sempahore will be set to the non-signalled state. This would mean
// that any other process coming along and performing a wait would be blocked until the sempahore
// is signalled once again.
//
// This function returns WAIT_FAILED if there was an error, WAIT_TIMEOUT if the wait operation timed out
// of WAIT_OBJECT_0 if the operation did in fact wait and then returned
///
//##ModelId=3DE6123B0277
UINT CSemaphore::Wait(DWORD Time) const // Handle of the semaphore needed
{
UINT Result = WaitForSingleObject(SemaphoreHandle, Time) ; // return WAIT_FAILED on error
PERR( Result != WAIT_FAILED, string("Cannot Wait on Semaphore: ") + SemaphoreName ) ; // check for error and print message if appropriate
return Result ;
}
//
// This function will attempt to 'signal' a semaphore. This will allow one or more blocked
// process to proceed (setting the mutex back to the non-signalled state), otherwise
// if no processes are waiting/blocked, the sempahore remains in the signalled state
//
//##ModelId=3DE6123B027F
BOOL CSemaphore::Signal( int Increment) const // value by which sempahore increases (default is 1)
{ // return TRUE/FALSE on Success/Failure
BOOL Success = ReleaseSemaphore( SemaphoreHandle, Increment, NULL) ;
PERR( Success == TRUE, string("Cannot Signal Semaphore: ") + SemaphoreName + string("\nMaxmimum Value may have been exceeded")) ; // check for error and print message if appropriate
return Success ;
}
//
// This function reads the value of a Semaphore, without signalling or waiting on it
// it can be used to poll the value of the Semaphore to perhaps determine if it is safe to
// perform a wait without getting suspended/blocked
// returns WAIT_FAILED if there was an error