1 /*
2     Title:      Task farm for Multi-Threaded Garbage Collector
3 
4     Copyright (c) 2010-12, 2019 David C. J. Matthews
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2.1 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.
15 
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 
20 */
21 
22 #ifndef GCTASKFARM_H_INCLUDED
23 #define GCTASKFARM_H_INCLUDED
24 
25 #include "locking.h"
26 
27 // An empty class just used as an ID.
28 class GCTaskId {
29 
30 };
31 
32 extern GCTaskId *globalTask; // The ID used when a function is run immediately
33 
34 // Function for action.  The usual C++ approach would be to use an
35 // object pointer but that requires lots of small objects to be created
36 // and deleted.
37 typedef void (*gctask)(GCTaskId*, void*, void*);
38 
39 typedef struct {
40     gctask  task;
41     void    *arg1;
42     void    *arg2;
43 } queue_entry;
44 
45 class GCTaskFarm {
46 public:
47     GCTaskFarm();
48     ~GCTaskFarm();
49 
50     // Initialise and create the worker threads
51     bool Initialise(unsigned threadCount, unsigned queueSize);
52     // Set single threaded mode. This is only used in a child process after
53     // Posix fork in case there is a GC before the exec.
SetSingleThreaded()54     void SetSingleThreaded() { threadCount = 0; queueSize = 0; }
55 
56     bool AddWork(gctask task, void *arg1, void *arg2);
57     void AddWorkOrRunNow(gctask task, void *arg1, void *arg2);
58     void WaitForCompletion(void);
59     void Terminate(void);
60     // See if the queue is draining.  Used as a hint as to whether
61     // it's worth sparking off some new work.
Draining(void)62     bool Draining(void) const { return queuedItems == 0; }
63 
ThreadCount(void)64     unsigned ThreadCount(void) const { return threadCount; }
65 
66 private:
67     // The semaphore is zero if there is no work or some value up to
68     // the number of threads if there is work.
69     PSemaphore waitForWork;
70     // The lock protects the queue and the item count.
71     PLock workLock;
72     // The condition variable is signalled when the queue is empty.
73     // This can only be waited for by a single thread because it's not a proper
74     // implementation of a condition variable in Windows.
75     PCondVar waitForCompletion;
76     unsigned queueSize, queueIn, queuedItems;
77     queue_entry *workQueue; // Array of unit->unit functions.
78     bool terminate; // Set to true to kill all workers.
79     unsigned threadCount; // Count of workers.
80     unsigned activeThreadCount; // Count of workers doing work.
81 
82     void ThreadFunction(void);
83 
84 #if (!defined(_WIN32))
85     static void *WorkerThreadFunction(void *parameter);
86     pthread_t *threadHandles;
87 #else
88     static DWORD WINAPI WorkerThreadFunction(void *parameter);
89     HANDLE *threadHandles;
90 #endif
91 };
92 
93 #endif
94 
95