aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/threads/thread.h
blob: 6319e38102092e8b1e0a838e716d708ead337871 (plain)
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
#ifndef __JE_THREAD_H__
#define __JE_THREAD_H__
#include "../core/configuration.h"
#if defined(jin_thread)

#include <string>
#include <map>
#if jin_thread == jin_thread_sdl 
	#include "SDL2/SDL_thread.h"
#elif jin_thread == jin_thread_cpp 
	#include <thread>
	#include <mutex>
	#include <condition_variable>
#endif

namespace JinEngine
{
	namespace Threads
	{
		/**
		 * ӢӢMutual exclusionд Mutexһڶ̱߳Уֹ߳ͬʱͬһԴ
		 * ȫֱждĻơĿͨƬһһٽcritical sectionɡٽ
		 * ָһԹԴзʵĴ룬һֻƻ㷨һ򡢽̡߳̿ӵжٽ򣬵Dz
		 * һӦûҪ˻ƵԴУꡢСжϴڶеĴ
		 * ݡͬ״̬ȵԴάЩԴͬһºǺѵģΪһ߳̿κһʱ̱ͣ
		 * ߣ߻ָѣ
		 */
		class Mutex;
		class Conditional;

		//
		// Thread::demand	Receive a message from a thread. Wait for the message to exist before returning.	
		// Thread::getName  Get the name of a thread.	
		// Thread::kill		Forcefully terminate the thread.	
		// Thread::peek		Receive a message from a thread, but leave it in the message box.	
		// Thread::receive  Receive a message from a thread.	
		// Thread::send		Send a message.	
		// Thread::set		Set a value.	
		// Thread::start	Starts the thread.	
		// Thread::wait		Wait for a thread to finish.
		//
		class Thread
		{
		public:
			struct Variant
			{
				enum Type
				{
					NONE = 0,
					INTERGER, 
					BOOLEAN, 
					CHARACTER, 
					CSTRING, 
					POINTER, 
					REAL,
				};
				Type type;
				union 
				{
					int integer;
					bool boolean;
					char character;
					const char* cstring;
					void* pointer;
					float real;
				};
				Variant() :type(NONE) {};
				Variant(const Variant& v){ memcpy(this, &v, sizeof(v)); }
				Variant(int i) : integer(i), type(INTERGER) {};
				Variant(float f) : real(f), type(REAL) {};
				Variant(bool b) : boolean(b), type(BOOLEAN) {};
				Variant(char c) : character(c), type(CHARACTER) {};
				Variant(const char* s) : cstring(s), type(CSTRING) {};
				Variant(void* p) : pointer(p), type(POINTER) {};
			};

		private:
			class ThreadData
			{
			public:
				static const int SLOT_ERROR = -1;
				static const int SLOT_WARN = -2;
				static const int SLOT_INFO = -3;
				static const int SLOT_DEBUG = -4;

				ThreadData(Mutex*, Conditional*);
				~ThreadData();
				bool exist(int slot);
				void set(int slot, Variant value);
				Variant get(int slot);
				void remove(int slot);

				Conditional* condition;
				Mutex* mutex;

			private:
				std::map<int, Variant> share; // threads shared value

			};

		public:
			typedef int(*ThreadRunner)(void* obj);

			Thread(const std::string name, ThreadRunner threadfuncs);
			~Thread();
			bool start(void* p);
			void wait();
			void send(int slot, const Variant& value);
			bool receive(int slot);
			Variant fetch(int slot);
			Variant demand(int slot);
			void remove(int slot);
			const char* getName();
			bool isRunning();
			void lock();
			void unlock();

		protected:
		#if jin_thread == jin_thread_sdl 
			SDL_Thread* handle;		 // SDL thread
		#elif jin_thread == jin_thread_cpp 
			std::thread* handle;		// cpp thread
		#endif
			Mutex* mutex;			   // mutex variable
			Conditional* condition;	 // condition variable
			ThreadRunner threadRunner; // thread function 
			ThreadData* common;		 // threads common data
			const std::string name;	 // thread name, for debugging purposes 
			/**
			 * https://stackoverflow.com/questions/149932/naming-conventions-for-threads
			 *
			 * Use short names because they don't make the lines in a log file too long.
			 * 
			 * Create names where the important part is at the beginning. Log viewers in a 
			 * graphical user interface tend to have tables with columns, and the thread 
			 * column is usually small or will be made small by you to read everything else.
			 * 
			 * Do not use the word "thread" in the thread name because it is obvious.
			 * 
			 * Make the thread names easily grep-able. Avoid similar sounding thread names
			 * 
			 * If you have several threads of the same nature, enumerate them with IDs that 
			 * are unique to one execution of the application or one log file, whichever fits 
			 * your logging habits.
			 * 
			 * Avoid generalizations like "WorkerThread" (how do you name the next 5 worker 
			 * threads?), "GUIThread" (which GUI? is it for one window? for everything?) or 
			 * "Calculation" (what does it calculate?).
			 * 
			 * If you have a test group that uses thread names to grep your application's log 
			 * files, do not rename your threads after some time. Your testers will hate you for 
			 * doing so. Thread names in well-tested applications should be there to stay.
			 * 
			 * When you have threads that service a network connection, try to include the target 
			 * network address in the thread name (e.g. channel_123.212.123.3). Don't forget about 
			 * enumeration though if there are multiple connections to the same host.
			 */
			bool running;			   // running

		};

	} // namespace Threads
} // namespace JinEngine

#endif // defined(jin_thread)

#endif // __JE_THREAD_H__