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  }