summaryrefslogtreecommitdiff
path: root/src/fiber_thread.cpp
blob: 6d311290c84dcdb9b2a6df3ffee7374fa5aac035 (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
/* $Id$ */

/** @file fiber_thread.cpp ThreadObject implementation of Fiber. */

#include "stdafx.h"
#include "fiber.hpp"
#include "thread.h"
#include <stdlib.h>

class Fiber_Thread : public Fiber {
private:
	ThreadObject *m_thread;
	FiberFunc m_proc;
	void *m_param;
	bool m_attached;
	ThreadSemaphore *m_sem;
	bool m_kill;

	static Fiber_Thread *s_current;
	static Fiber_Thread *s_main;

public:
	/**
	 * Create a ThreadObject fiber and start it, calling proc(param).
	 */
	Fiber_Thread(FiberFunc proc, void *param) :
		m_thread(NULL),
		m_proc(proc),
		m_param(param),
		m_attached(false),
		m_kill(false)
	{
		this->m_sem = ThreadSemaphore::New();
		/* Create a thread and start stFiberProc */
		this->m_thread = ThreadObject::New(&stFiberProc, this);
	}

	/**
	 * Create a ThreadObject fiber and attach current thread to it.
	 */
	Fiber_Thread(void *param) :
		m_thread(NULL),
		m_proc(NULL),
		m_param(param),
		m_attached(true),
		m_kill(false)
	{
		this->m_sem = ThreadSemaphore::New();
		/* Attach the current thread to this Fiber */
		this->m_thread = ThreadObject::AttachCurrent();
		/* We are the current thread */
		if (s_current == NULL) s_current = this;
		if (s_main == NULL) s_main = this;
	}

	~Fiber_Thread()
	{
		/* Remove the thread if needed */
		if (this->m_thread != NULL) {
			assert(this->m_attached || !this->m_thread->IsRunning());
			delete this->m_thread;
		}
		/* Remove the semaphore */
		delete this->m_sem;
	}

	/* virtual */ void SwitchToFiber()
	{
		/* You can't switch to yourself */
		assert(s_current != this);
		Fiber_Thread *cur = s_current;

		/* Continue the execution of 'this' Fiber */
		this->m_sem->Set();
		/* Hold the execution of the current Fiber */
		cur->m_sem->Wait();
		if (this->m_kill) {
			/* If the thread we switched too was killed, join it so it can finish quiting */
			this->m_thread->Join();
		}
		/* If we continue, we are the current thread */
		s_current = cur;
	}

	/* virtual */ void Exit()
	{
		/* Kill off our thread */
		this->m_kill = true;
		this->m_thread->Exit();
	}

	/* virtual */ bool IsRunning()
	{
		if (this->m_thread == NULL) return false;
		return this->m_thread->IsRunning();
	}

	/* virtual */ void *GetFiberData()
	{
		return this->m_param;
	}

	static Fiber_Thread *GetCurrentFiber()
	{
		return s_current;
	}

private:
	/**
	 * First function which is called within the fiber.
	 */
	static void * CDECL stFiberProc(void *fiber)
	{
		Fiber_Thread *cur = (Fiber_Thread *)fiber;
		/* Now suspend the thread until we get SwitchToFiber() for the first time */
		cur->m_sem->Wait();
		/* If we continue, we are the current thread */
		s_current = cur;

		try {
			cur->m_proc(cur->m_param);
		} catch (...) {
			/* Unlock the main thread */
			s_main->m_sem->Set();
			throw;
		}

		return NULL;
	}
};

/* Initialize the static member of Fiber_Thread */
/* static */ Fiber_Thread *Fiber_Thread::s_current = NULL;
/* static */ Fiber_Thread *Fiber_Thread::s_main = NULL;

#ifndef WIN32

/* static */ Fiber *Fiber::New(FiberFunc proc, void *param)
{
	return new Fiber_Thread(proc, param);
}

/* static */ Fiber *Fiber::AttachCurrent(void *param)
{
	return new Fiber_Thread(param);
}

/* static */ void *Fiber::GetCurrentFiberData()
{
	return Fiber_Thread::GetCurrentFiber()->GetFiberData();
}

#endif /* WIN32 */