1 /* 2 Title: Lightweight process library 3 Author: David C.J. Matthews 4 5 Copyright (c) 2007-8, 2012, 2015, 2017, 2019, 2020 David C.J. Matthews 6 7 This library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License version 2.1 as published by the Free Software Foundation. 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 _PROCESSES_H_ 23 #define _PROCESSES_H_ 24 25 #ifdef HAVE_CONFIG_H 26 #include "config.h" 27 #elif defined(_WIN32) 28 #include "winconfig.h" 29 #else 30 #error "No configuration file" 31 #endif 32 33 #include "globals.h" 34 #include "rts_module.h" 35 #include "save_vec.h" 36 37 #include "noreturn.h" 38 #include "locking.h" 39 40 class SaveVecEntry; 41 typedef SaveVecEntry *Handle; 42 class StackSpace; 43 class PolyWord; 44 class ScanAddress; 45 class MDTaskData; 46 class Exporter; 47 class StackObject; 48 49 #ifdef HAVE_WINDOWS_H 50 typedef void *HANDLE; 51 #endif 52 53 #ifdef HAVE_SIGNAL_H 54 #include <signal.h> 55 #endif 56 57 #ifdef HAVE_UCONTEXT_H 58 #include <ucontext.h> 59 #endif 60 61 #ifdef HAVE_PTHREAD_H 62 #include <pthread.h> 63 #endif 64 65 // SIGNALCONTEXT is the argument type that is passed to GetPCandSPFromContext 66 // to get the actual PC and SP in a profiling trap. 67 #if defined(HAVE_WINDOWS_H) 68 // First because it's used in both native Windows and Cygwin. 69 #include <windows.h> 70 #define SIGNALCONTEXT CONTEXT // This is the thread context. 71 #elif defined(HAVE_UCONTEXT_T) 72 #define SIGNALCONTEXT ucontext_t 73 #elif defined(HAVE_STRUCT_SIGCONTEXT) 74 #define SIGNALCONTEXT struct sigcontext 75 #else 76 #define SIGNALCONTEXT void 77 #endif 78 79 #define MIN_HEAP_SIZE 4096 // Minimum and initial heap segment size (words) 80 81 // This is the ML "thread identifier" object. The fields 82 // are read and set by the ML code. 83 class ThreadObject: public PolyObject { 84 public: 85 PolyWord threadRef; // Weak ref containing the address of the thread data. Not used by ML 86 PolyWord flags; // Tagged integer containing flags indicating how interrupts 87 // are handled. Set by ML but only by the thread itself 88 PolyWord threadLocal; // Head of a list of thread-local store items. 89 // Handled entirely by ML but only by the thread. 90 PolyWord requestCopy; // A tagged integer copy of the "requests" field. 91 // This is provided so that ML can easily test if there 92 // is an interrupt pending. 93 PolyWord mlStackSize; // A tagged integer with the maximum ML stack size in bytes 94 PolyWord debuggerSlots[4]; // These are used by the debugger. 95 }; 96 97 // Other threads may make requests to a thread. 98 typedef enum { 99 kRequestNone = 0, // Increasing severity 100 kRequestInterrupt = 1, 101 kRequestKill = 2 102 } ThreadRequests; 103 104 // Per-thread data. This is subclassed for each architecture. 105 class TaskData { 106 public: 107 TaskData(); 108 virtual ~TaskData(); 109 110 void FillUnusedSpace(void); 111 virtual void GarbageCollect(ScanAddress *process); 112 113 virtual void EnterPolyCode() = 0; // Start running ML 114 115 virtual void InterruptCode() = 0; 116 virtual bool AddTimeProfileCount(SIGNALCONTEXT *context) = 0; 117 // Initialise the stack for a new thread. The parent task object is passed in because any 118 // allocation that needs to be made must be made in the parent. 119 virtual void InitStackFrame(TaskData *parentTask, Handle proc, Handle arg) = 0; 120 virtual void SetException(poly_exn *exc) = 0; 121 122 // The scheduler needs versions of atomic decrement and atomic reset that 123 // work in exactly the same way as the code-generated versions (if any). 124 // Atomic decrement isn't needed since it only ever releases a mutex. 125 virtual Handle AtomicDecrement(Handle mutexp) = 0; 126 // Reset a mutex to one. This needs to be atomic with respect to the 127 // atomic increment and decrement instructions. 128 virtual void AtomicReset(Handle mutexp) = 0; 129 130 virtual void CopyStackFrame(StackObject *old_stack, uintptr_t old_length, 131 StackObject *new_stack, uintptr_t new_length) = 0; 132 133 134 virtual uintptr_t currentStackSpace(void) const = 0; 135 // Add a count to the local function if we are using store profiling. 136 virtual void addProfileCount(POLYUNSIGNED words) = 0; 137 138 // Functions called before and after an RTS call. PreRTSCall(void)139 virtual void PreRTSCall(void) { inML = false; } PostRTSCall(void)140 virtual void PostRTSCall(void) { inML = true; } 141 142 SaveVec saveVec; 143 PolyWord *allocPointer; // Allocation pointer - decremented towards... 144 PolyWord *allocLimit; // ... lower limit of allocation 145 uintptr_t allocSize; // The preferred heap segment size 146 unsigned allocCount; // The number of allocations since the last GC 147 StackSpace *stack; 148 ThreadObject *threadObject; // Pointer to the thread object. 149 int lastError; // Last error from foreign code. 150 void *signalStack; // Stack to handle interrupts (Unix only) 151 bool inML; // True when this is in ML, false in the RTS 152 153 // Get a TaskData pointer given the ML taskId. 154 // This is called at the start of every RTS function that may allocate memory. 155 // It is can be called safely to get the thread's own TaskData object without 156 // a lock but any call to get the TaskData for another thread must take the 157 // schedLock first in case the thread is exiting. FindTaskForId(PolyWord taskId)158 static TaskData *FindTaskForId(PolyWord taskId) { 159 return *(TaskData**)(((ThreadObject*)taskId.AsObjPtr())->threadRef.AsObjPtr()); 160 } 161 162 private: 163 // If a thread has to block it will block on this. 164 PCondVar threadLock; 165 // External requests made are stored here until they 166 // can be actioned. 167 ThreadRequests requests; 168 // Pointer to the mutex when blocked. Set to NULL when it doesn't apply. 169 PolyObject *blockMutex; 170 // This is set to false when a thread blocks or enters foreign code, 171 // While it is true the thread can manipulate ML memory so no other 172 // thread can garbage collect. 173 bool inMLHeap; 174 175 // In Linux, at least, we need to run a separate timer in each thread 176 bool runningProfileTimer; 177 178 #ifdef HAVE_WINDOWS_H 179 LONGLONG lastCPUTime; // Used for profiling 180 #endif 181 public: 182 bool threadExited; 183 private: 184 #ifdef HAVE_PTHREAD_H 185 pthread_t threadId; 186 #endif 187 #ifdef HAVE_WINDOWS_H 188 public: // Because, on Cygwin, it's used in NewThreadFunction 189 HANDLE threadHandle; 190 private: 191 #endif 192 193 friend class Processes; 194 }; 195 196 NORETURNFN(extern Handle exitThread(TaskData *mdTaskData)); 197 198 class ScanAddress; 199 200 // Indicate what the main thread is doing if the profile 201 // timer goes off. 202 extern enum _mainThreadPhase { 203 MTP_USER_CODE=0, 204 MTP_GCPHASESHARING, 205 MTP_GCPHASEMARK, 206 MTP_GCPHASECOMPACT, 207 MTP_GCPHASEUPDATE, 208 MTP_GCQUICK, 209 MTP_SHARING, 210 MTP_EXPORTING, 211 MTP_SAVESTATE, 212 MTP_LOADSTATE, 213 MTP_PROFILING, 214 MTP_SIGHANDLER, 215 MTP_CYGWINSPAWN, 216 MTP_STOREMODULE, 217 MTP_LOADMODULE, 218 MTP_MAXENTRY 219 } mainThreadPhase; 220 221 // Data structure used for requests from a thread to the root 222 // thread. These are GCs or similar. 223 class MainThreadRequest 224 { 225 public: MainThreadRequest(enum _mainThreadPhase phase)226 MainThreadRequest (enum _mainThreadPhase phase): mtp(phase), completed(false) {} ~MainThreadRequest()227 virtual ~MainThreadRequest () {} // Suppress silly GCC warning 228 const enum _mainThreadPhase mtp; 229 bool completed; 230 virtual void Perform() = 0; 231 }; 232 233 class PLock; 234 235 // Class to wait for a given time or for an event, whichever comes first. 236 // 237 // A pointer to this class or a subclass is passed to ThreadPauseForIO. 238 // Because a thread may be interrupted or killed by another ML thread we 239 // don't allow any thread to block indefinitely. Instead whenever a 240 // thread wants to do an operation that may block we have it enter a 241 // loop that polls for the desired condition and if it is not ready it 242 // calls ThreadPauseForIO. The default action is to block for a short 243 // period and then return so that the caller can poll again. That can 244 // limit performance when, for example, reading from a pipe so where possible 245 // we use a sub-class that waits until either input is available or it times 246 // out, whichever comes first, using "select" in Unix or MsgWaitForMultipleObjects 247 // in Windows. 248 // During a call to Waiter::Wait the thread is set as "not using ML memory" 249 // so a GC can happen while this thread is blocked. 250 class Waiter 251 { 252 public: Waiter()253 Waiter() {} ~Waiter()254 virtual ~Waiter() {} 255 virtual void Wait(unsigned maxMillisecs); 256 static Waiter *defaultWaiter; 257 }; 258 259 #ifdef _WIN32 260 class WaitHandle: public Waiter 261 { 262 public: WaitHandle(HANDLE h,unsigned maxWait)263 WaitHandle(HANDLE h, unsigned maxWait): m_Handle(h), m_maxWait(maxWait) {} 264 virtual void Wait(unsigned maxMillisecs); 265 private: 266 HANDLE m_Handle; 267 unsigned m_maxWait; 268 }; 269 270 #else 271 272 // Unix: Wait until a file descriptor is available for input 273 class WaitInputFD: public Waiter 274 { 275 public: WaitInputFD(int fd)276 WaitInputFD(int fd): m_waitFD(fd) {} 277 virtual void Wait(unsigned maxMillisecs); 278 private: 279 int m_waitFD; 280 }; 281 #endif 282 283 284 // External interface to the Process module. These functions are all implemented 285 // by the Processes class. 286 class ProcessExternal 287 { 288 public: ~ProcessExternal()289 virtual ~ProcessExternal() {} // Defined to suppress a warning from GCC 290 291 virtual TaskData *GetTaskDataForThread(void) = 0; 292 virtual TaskData *CreateNewTaskData(Handle threadId, Handle threadFunction, 293 Handle args, PolyWord flags) = 0; 294 // Request all ML threads to exit and set the result code. Does not cause 295 // the calling thread itself to exit since this may be called on the GUI thread. 296 virtual void RequestProcessExit(int n) = 0; 297 // Exit from this thread. 298 virtual NORETURNFN(void ThreadExit(TaskData *taskData)) = 0; 299 300 virtual void BroadcastInterrupt(void) = 0; 301 302 virtual void BeginRootThread(PolyObject *rootFunction) = 0; 303 304 // Called when a thread may block. Returns some time later when perhaps 305 // the input is available. 306 virtual void ThreadPauseForIO(TaskData *taskData, Waiter *pWait) = 0; 307 // As ThreadPauseForIO but when there is no stream ThreadPause(TaskData * taskData)308 virtual void ThreadPause(TaskData *taskData) { ThreadPauseForIO(taskData, Waiter::defaultWaiter); } 309 310 // If a thread is blocking for some time it should release its use 311 // of the ML memory. That allows a GC. ThreadUseMLMemory returns true if 312 // a GC was in progress. 313 virtual void ThreadUseMLMemory(TaskData *taskData) = 0; 314 virtual void ThreadReleaseMLMemory(TaskData *taskData) = 0; 315 316 // Requests from the threads for actions that need to be performed by 317 // the root thread. 318 virtual void MakeRootRequest(TaskData *taskData, MainThreadRequest *request) = 0; 319 320 // Deal with any interrupt or kill requests. 321 virtual bool ProcessAsynchRequests(TaskData *taskData) = 0; 322 // Process an interrupt request synchronously. 323 virtual void TestSynchronousRequests(TaskData *taskData) = 0; 324 // Process any events, synchronous or asynchronous. 325 virtual void TestAnyEvents(TaskData *taskData) = 0; 326 327 // ForkFromRTS. Creates a new thread from within the RTS. 328 virtual bool ForkFromRTS(TaskData *taskData, Handle proc, Handle arg) = 0; 329 330 // Profiling control. 331 virtual void StartProfiling(void) = 0; 332 virtual void StopProfiling(void) = 0; 333 334 // Find space for an object. Returns a pointer to the start. "words" must include 335 // the length word and the result points at where the length word will go. 336 // If the allocation succeeds it may update the allocation values in the taskData object. 337 // If the heap is exhausted it may set this thread (or other threads) to raise an exception. 338 virtual PolyWord *FindAllocationSpace(TaskData *taskData, POLYUNSIGNED words, bool alwaysInSeg) = 0; 339 340 // Signal handling support. The ML signal handler thread blocks until it is 341 // woken up by the signal detection thread. 342 virtual bool WaitForSignal(TaskData *taskData, PLock *sigLock) = 0; 343 virtual void SignalArrived(void) = 0; 344 345 virtual poly_exn* GetInterrupt(void) = 0; 346 }; 347 348 // Return the number of processors. Used when configuring multi-threaded GC. 349 extern unsigned NumberOfProcessors(void); 350 extern unsigned NumberOfPhysicalProcessors(void); 351 352 extern ProcessExternal *processes; 353 354 extern struct _entrypts processesEPT[]; 355 356 #endif 357