1 package frost.unsafe
2
3 ====================================================================================================
4 A message queue which allows mutable objects to be passed between threads, violating the normal
5 Frost assumption that a mutable object is accessible only from a single thread.
6
7 This is **much more dangerous** than one would assume at first glance, since *any* interaction with
8 a mutable object passed between threads is dangerous. As Frost assumes that mutable objects are not
9 shared between threads, it does not take any measures to ensure thread safety while refcounting
10 objects that are known at compile time to be mutable. Even read-only multithreaded access to mutable
11 objects can cause undefined behavior in the absence of very careful locking.
12 ====================================================================================================
13 @unsafeImmutable
14 class UnsafeMessageQueue<T> : Immutable {
15 @private
16 class Message<T> {
17 def payload:T
18
19 var next:Message<T>?
20
21 init(payload:T) {
22 self.payload := payload
23 }
24
25 @override
26 function get_toString():String {
27 if next !== null {
28 return "Message(\{payload}, \{next})"
29 }
30 return "Message(\{payload})"
31 }
32 }
33
34 @private
35 def lock := Lock()
36
37 @private
38 def notifier := Notifier(lock)
39
40 @private
41 var count := 0
42
43 @private
44 var head:Message<T>?
45
46 @private
47 var tail:Message<T>?
48
49 ================================================================================================
50 Posts an object to the queue.
51
52 @param data the object to post
53 ================================================================================================
54 method post(data:T) {
55 def scope := ScopedLock(lock)
56 if tail !== null {
57 assert head !== null
58 tail!.next := Message<T>(data)
59 tail := tail!.next
60 }
61 else {
62 assert count = 0
63 head := Message<T>(data)
64 tail := head
65 }
66 count += 1
67 notifier.notify()
68 }
69
70 ================================================================================================
71 Returns the number of messages currently in the queue.
72
73 @returns the number of messages in this `MessageQueue`
74 @see hasMessage()
75 @see getMessage()
76 ================================================================================================
77 method pendingMessages():Int {
78 def scope := ScopedLock(lock)
79 return count
80 }
81
82 ================================================================================================
83 Returns `true` if there are one or more messages in the queue.
84
85 @returns whether this queue has pending messages
86 @see pendingMessages()
87 @see getMessage()
88 ================================================================================================
89 method hasMessage():Bit {
90 return pendingMessages() > 0
91 }
92
93 ================================================================================================
94 Returns the next message from the queue, blocking until one is available.
95
96 @returns the next message from the queue
97 ================================================================================================
98 method getMessage():T {
99 def scope := ScopedLock(lock)
100 while head == null {
101 notifier.wait()
102 }
103 def result := head.payload
104 head := head.next
105 if head == null {
106 tail := null
107 }
108 count -= 1
109 return result
110 }
111
112 ================================================================================================
113 Remove all pending messages from the queue.
114 ================================================================================================
115 method clear() {
116 while hasMessage() {
117 getMessage()
118 }
119 }
120 }