Class: Tuile::EventQueue

Inherits:
Object
  • Object
show all
Defined in:
lib/tuile/event_queue.rb

Overview

An event queue. The idea is that all UI-related updates run from the thread which runs the event queue only; this removes any need for locking and/or need for thread-safety mechanisms.

Any events (keypress, timer, term resize – WINCH) are captured in background threads; instead of processing the events directly the events are pushed into the event queue: this causes the events to be processed centrally, by a single thread only.

Defined Under Namespace

Classes: EmptyQueueEvent, ErrorEvent, KeyEvent, TTYSizeEvent

Instance Method Summary collapse

Constructor Details

#initialize(listen_for_keys: true) ⇒ EventQueue

Returns a new instance of EventQueue.

Parameters:

  • listen_for_keys (Boolean) (defaults to: true)

    if true, fires KeyEvent.



14
15
16
17
18
# File 'lib/tuile/event_queue.rb', line 14

def initialize(listen_for_keys: true)
  @queue = Thread::Queue.new
  @listen_for_keys = listen_for_keys
  @run_lock = Mutex.new
end

Instance Method Details

#await_emptyvoid

This method returns an undefined value.

Awaits until the event queue is empty (all events have been processed).



44
45
46
47
48
# File 'lib/tuile/event_queue.rb', line 44

def await_empty
  latch = Concurrent::CountDownLatch.new(1)
  submit { latch.count_down }
  latch.wait
end

#locked?Boolean

Returns true if this thread is running inside an event queue.

Returns:

  • (Boolean)

    true if this thread is running inside an event queue.



80
# File 'lib/tuile/event_queue.rb', line 80

def locked? = @run_lock.owned?

#post(event) ⇒ void

This method returns an undefined value.

Posts event into the event queue. The event may be of any type. Since the event is passed between threads, the event object should be frozen.

The function may be called from any thread.

Parameters:

  • event (Object)

    the event to post to the queue, should be frozen.

Raises:

  • (ArgumentError)


26
27
28
29
30
# File 'lib/tuile/event_queue.rb', line 26

def post(event)
  raise ArgumentError, "event passed across threads must be frozen, got #{event.inspect}" unless event.frozen?

  @queue << event
end

#run_loop {|event| ... } ⇒ void

This method returns an undefined value.

Runs the event loop and blocks. Must be run from at most one thread at the same time. Blocks until some thread calls #stop. Calls block for all events submitted via #post; the block is always called from the thread running this function.

Any exception raised by block is re-thrown, causing this function to terminate.

Yields:

  • (event)

    called for each non-internal event.

Yield Parameters:

Yield Returns:

  • (void)

Raises:

  • (ArgumentError)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/tuile/event_queue.rb', line 63

def run_loop(&)
  raise ArgumentError, "run_loop requires a block" unless block_given?

  @run_lock.synchronize do
    start_key_thread if @listen_for_keys
    begin
      trap_winch
      event_loop(&)
    ensure
      Signal.trap("WINCH", "SYSTEM_DEFAULT")
      @key_thread&.kill
      @queue.clear
    end
  end
end

#stopvoid

This method returns an undefined value.

Stops ongoing #run_loop. The stop may not be immediate: #run_loop may process a bunch of events before terminating.

Can be called from any thread, including the thread which runs the event loop.



88
89
90
91
# File 'lib/tuile/event_queue.rb', line 88

def stop
  @queue.clear
  post(nil)
end

#submit { ... } ⇒ void

This method returns an undefined value.

Submits block to be run in the event queue. Returns immediately.

The function may be called from any thread.

Yields:

  • called from the event-loop thread.

Yield Returns:

  • (void)


38
39
40
# File 'lib/tuile/event_queue.rb', line 38

def submit(&block)
  @queue << block
end