File: ThreadData.h

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (142 lines) | stat: -rw-r--r-- 5,088 bytes parent folder | download
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
#pragma once
#include "Utils/Function.h"
#include "Utils/Semaphore.h"
#include "IOCondition.h"
#include "IOHandle.h"
#include "UThread.h"
#include "InlineSet.h"

namespace os {

	// Internal data.
	class ThreadData;
	class ThreadStart;

	// Interface for interaction with OS-specific waits. See below.
	class ThreadWait;

	// Thread group.
	class ThreadGroup;

	/**
	 * Internal thread data.
	 */
	class ThreadData : public SetMember<ThreadData> {
	public:
		// Number of references.
		nat references;

		// Data used for the UThreads. Make sure to always run constructors and destructors
		// from the thread this ThreadData is representing.
		UThreadState uState;

		// Create.
		ThreadData(void *stackBase, void *osThreadData);

		// Destroy.
		~ThreadData();

		// Add refcount.
		inline void addRef() {
			atomicIncrement(references);
		}

		// Release.
		inline void release() {
			if (atomicDecrement(references) == 0)
				reportZero();
		}

		// Report that an UThread has been awoken and wants to be scheduled.
		void reportWake();

		// Wait for another UThread to be scheduled. Returns 'true' as long as the 'wait' structure is used.
		bool waitForWork();

		// Check if there is any IO completion we shall handle.
		void checkIo();

		// Attach a handle.
		inline void attach(Handle h) { ioComplete.add(h, this); }

		// Detach a handle.
		inline void detach(Handle h) { ioComplete.remove(h, this); }

		// Access the OS-specific data. This is currently only used on 32-bit Windows systems, where
		// we need to keep track of the top element of the SEH handler chain in order to work with
		// the SEHOP mitigation. See UThread.cpp for more details.
		inline void *osExtraData() { return osExtra; }

		// Thread main function.
		static void threadMain(ThreadStart &start, void *stackBottom);

	private:
		friend class Thread;
		friend class IORequest;

		// Condition variable for waking up the thread when there is more work to do
		// or when it is time to exit.
		IOCondition wakeCond;

		// Current wait behavior.
		ThreadWait *wait;

		// Other os-specific data that needs to be remembered for this thread.
		void *osExtra;

		// Handle indicating the completion of any async IO operations.
		IOHandle ioComplete;

		// Report zero references.
		void reportZero();
	};


	/**
	 * Thread wait logic. Implement this to interact better with other events from the OS for a
	 * specific thread. Spawning a thread using a ThreadWait interface lets you control how long the
	 * thread should be kept alive, and allows you to implement custom waiting conditions.
	 */
	class ThreadWait {
	public:
		// The destructor will always be executed in the thread that has been 'wait'ing on this object.
		virtual ~ThreadWait();

		// Called before any work is done, on the thread that will call wait later on. Note that the
		// constructor will probably _not_ run on the same thread as 'init' will be run on.
		// This function is called to perform enough initialization so that 'signal' will work properly.
		// Therefore, 'init' is called while the creating thread is blocked. Because of this, do not call
		// any functions in Storm here, as they may cause a deadlock. Defer such work to 'setup' instead.
		virtual void init();

		// Called before the first thread switch on the newly created thread, but after the spawning
		// thread has been released. Therefore, it is safe to call any function inside Storm from
		// here, but keep in mind that 'signal' might be called during the call to 'signal'.
		virtual void setup();

		// Called when the thread should wait for an event of some kind. This functions should
		// return either when 'signal' has been called, but may return in other cases as well.
		// The thread is kept alive until 'wait' returns false. At this point, 'wait' will not be
		// called any more, and the ThreadWait will eventually be destroyed.
		// The passed handle shall also be examined and the wait shall be aborted if that becomes signaling.
		// NOTE: It is vital that the 'wait' operation does not call any code that uses
		// synchronization primitives in Sync.h or similar, such as dispatching events from an UI
		// thread or similar. Doing this violates the core assumption that a sleeping thread will
		// not try to wait for other things in the system. To the threading system, this appears
		// that a sleeping thread is running, which causes havoc. Primitives dealing with the entire
		// OS thread (as opposed to a single UThread) are, fine (and necessary) to use though.
		virtual bool wait(IOHandle &io) = 0;

		// Called when wait() should be called, but when a timeout is also present.
		virtual bool wait(IOHandle &io, nat msTimeout) = 0;

		// Called to indicate that any thread held by 'wait' should be awoken. May be called from
		// any thread. Calls to 'signal' after the last call to 'wait' may occur.
		virtual void signal() = 0;

		// Called from the root UThread as per the regular round-robin fashion. Will not be called
		// after 'wait' has returned false. Default implementation does nothing.
		virtual void work();
	};

}