1 module libasync.signal;
2 import std.traits;
3 
4 import libasync.types;
5 import libasync.events;
6 import core.thread;
7 
8 /// Enqueues a signal in the event loop of the AsyncSignal owner's thread, 
9 /// which allows a foreign thread to trigger the callback handler safely.
10 shared final class AsyncSignal
11 {
12 	private void delegate() m_sgh;
13 nothrow:
14 private:
15 	Thread m_owner;
16 	EventLoop m_evLoop;
17 	fd_t m_evId;
18 
19 public:
20 
21 	this(EventLoop evl) 
22 	in {
23 		assert(evl !is null);
24 	}
25 	body {
26 		m_evLoop = cast(shared) evl;
27 		import core.thread : Thread;
28 		m_owner = cast(shared) Thread.getThis();
29 
30 		version(Posix) {
31 			static if (EPOLL) {
32 				import core.sys.posix.pthread : pthread_self;
33 				m_pthreadId = cast(shared)pthread_self();
34 			} else /* if KQUEUE */ {
35 				m_owner_id = cast(shared) g_threadId;
36 			}
37 		}
38 	}
39 
40 	@property bool hasError() const 
41 	{
42 		return (cast(EventLoop)m_evLoop).status.code != Status.OK;
43 	}
44 
45 	/// Used to diagnose errors when run() or kill() returns false
46 	@property StatusInfo status() const {
47 		return (cast(EventLoop)m_evLoop).status;
48 	}
49 
50 	/// Human-readable string describing the error
51 	@property string error() const {
52 		return (cast(EventLoop)m_evLoop).error;
53 	}
54 
55 	/// Registers the signal handler in the event loop
56 	bool run(void delegate() del) 
57 	in {
58 		debug assert(Thread.getThis() is cast(Thread)m_owner);
59 	}
60 	body {
61 
62 		m_sgh = cast(void delegate()) del;
63 
64 		m_evId = (cast(EventLoop) m_evLoop).run(this);
65 		if (m_evId != fd_t.init)
66 			return true;
67 		else
68 			return false;
69 	}
70 
71 	/// Cleans up underlying resources. This object must be run() again afterwards to be valid
72 	bool kill() 
73 	in {
74 		debug assert(Thread.getThis() is cast(Thread)m_owner);
75 	}
76 	body {
77 		return (cast(EventLoop)m_evLoop).kill(cast(shared AsyncSignal) this);
78 	}
79 
80 	/// Triggers the handler in its local thread
81 	synchronized bool trigger(EventLoop evl) {
82 		return evl.notify(m_evId, this);
83 	}
84 
85 	/// ditto
86 	synchronized bool trigger() {
87 		return (cast(EventLoop)m_evLoop).notify(m_evId, this);
88 	}
89 
90 	/// Returns the Thread that created this object.
91 	synchronized @property Thread owner() const {
92 		return cast(Thread) m_owner;
93 	}
94 package:
95 	
96 
97 
98 	version(Posix) mixin EvInfoMixinsShared;
99 
100 	@property id() const {
101 		return m_evId;
102 	}
103 
104 	void handler() {
105 		try m_sgh();
106 		catch {}
107 		return;
108 	}
109 }
110 
111 package shared struct SignalHandler {
112 	AsyncSignal ctxt;
113 	void function(shared AsyncSignal) fct;
114 
115 	void opCall(shared AsyncSignal ctxt) {
116 		assert(ctxt !is null);
117 		fct(ctxt);
118 		return;
119 	}
120 }
121 
122 
123 /**
124 	Determines if the given list of types has any non-immutable and unshared aliasing outside of their object tree.
125 
126 	The types in particular may only contain plain data, pointers or arrays to immutable or shared data, or references
127 	encapsulated in stdx.typecons.Isolated. Values that do not have unshared and unisolated aliasing are safe to be passed
128 	between threads.
129 */
130 template isWeaklyIsolated(T...)
131 {
132 	import std.typecons : Rebindable;
133 	static if (T.length == 0) enum bool isWeaklyIsolated = true;
134 	else static if (T.length > 1) enum bool isWeaklyIsolated = isWeaklyIsolated!(T[0 .. $/2]) && isWeaklyIsolated!(T[$/2 .. $]);
135 	else {
136 		static if(is(T[0] == immutable)) enum bool isWeaklyIsolated = true;
137 		else static if (is(T[0] == shared)) enum bool isWeaklyIsolated = true;
138 		else static if (isInstanceOf!(Rebindable, T[0])) enum bool isWeaklyIsolated = isWeaklyIsolated!(typeof(T[0].get()));
139 		else static if (is(T[0] : Throwable)) enum bool isWeaklyIsolated = true; // WARNING: this is unsafe, but needed for send/receive!
140 		else static if (is(typeof(T[0].__isIsolatedType))) enum bool isWeaklyIsolated = true;
141 		else static if (is(typeof(T[0].__isWeakIsolatedType))) enum bool isWeaklyIsolated = true;
142 		else static if (is(T[0] == class)) enum bool isWeaklyIsolated = false;
143 		else static if (is(T[0] == interface)) enum bool isWeaklyIsolated = false; // can't know if the implementation is isolated
144 		else static if (is(T[0] == delegate)) enum bool isWeaklyIsolated = T[0].stringof.endsWith(" shared"); // can't know to what a delegate points - FIXME: use something better than a string comparison
145 		else static if (isDynamicArray!(T[0])) enum bool isWeaklyIsolated = is(typeof(T[0].init[0]) == immutable);
146 		else static if (isAssociativeArray!(T[0])) enum bool isWeaklyIsolated = false; // TODO: be less strict here
147 		else static if (isSomeFunction!(T[0])) enum bool isWeaklyIsolated = true; // functions are immutable
148 		else static if (isPointer!(T[0])) enum bool isWeaklyIsolated = is(typeof(*T[0].init) == immutable) || is(typeof(*T[0].init) == shared);
149 		else static if (isAggregateType!(T[0])) enum bool isWeaklyIsolated = isWeaklyIsolated!(FieldTypeTuple!(T[0]));
150 		else enum bool isWeaklyIsolated = true;
151 	}
152 }