Class: Syskit::RemoteStateGetter Private

Inherits:
Object
  • Object
show all
Defined in:
lib/syskit/remote_state_getter.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Adapter that reads the synchronous state of a remote node asynchronously

It is started in a paused state, waiting for #resume to be called to start polling the remote state

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(orocos_task, initial_state: nil, period: 0.02) ⇒ RemoteStateGetter

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of RemoteStateGetter



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/syskit/remote_state_getter.rb', line 32

def initialize(orocos_task, initial_state: nil, period: 0.02)
    @orocos_task = orocos_task
    @period = period
    @exit_condition = Concurrent::Event.new
    @run_event = Concurrent::Event.new
    @current_state = Concurrent::Atom.new(initial_state)
    @state_queue = Queue.new
    @sync_latch = Concurrent::Atom.new(nil)
    @poll_thread_error = Concurrent::Atom.new(nil)
    @poll_thread_exit_sync = Mutex.new
    @last_read_state = nil
    if initial_state
        state_queue.push(initial_state)
    end

    period = self.period
    exit_condition.reset
    @poll_thread = Thread.new do
        poll_loop
    end
end

Instance Attribute Details

#current_stateObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The newest known state



24
25
26
# File 'lib/syskit/remote_state_getter.rb', line 24

def current_state
  @current_state
end

#exit_conditionObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The condition used to make the polling thread quit



14
15
16
# File 'lib/syskit/remote_state_getter.rb', line 14

def exit_condition
  @exit_condition
end

#last_read_stateObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The last read state



26
27
28
# File 'lib/syskit/remote_state_getter.rb', line 26

def last_read_state
  @last_read_state
end

#orocos_taskObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The task we're polling



10
11
12
# File 'lib/syskit/remote_state_getter.rb', line 10

def orocos_task
  @orocos_task
end

#periodObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The polling period in seconds



12
13
14
# File 'lib/syskit/remote_state_getter.rb', line 12

def period
  @period
end

#poll_threadObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The thread that polls #rtt_state



16
17
18
# File 'lib/syskit/remote_state_getter.rb', line 16

def poll_thread
  @poll_thread
end

#poll_thread_errorObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The exception that terminated #poll_thread



18
19
20
# File 'lib/syskit/remote_state_getter.rb', line 18

def poll_thread_error
  @poll_thread_error
end

#poll_thread_exit_syncObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

To avoid deadlocking in #wait if the thread quits



30
31
32
# File 'lib/syskit/remote_state_getter.rb', line 30

def poll_thread_exit_sync
  @poll_thread_exit_sync
end

#run_eventObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Control for pause/resume



28
29
30
# File 'lib/syskit/remote_state_getter.rb', line 28

def run_event
  @run_event
end

#state_queueObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The queue of values read by #poll_thread



20
21
22
# File 'lib/syskit/remote_state_getter.rb', line 20

def state_queue
  @state_queue
end

#sync_latchObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Holder for the latch that is used by #wait for synchronization



22
23
24
# File 'lib/syskit/remote_state_getter.rb', line 22

def sync_latch
  @sync_latch
end

Instance Method Details

#clearObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



143
144
145
146
147
# File 'lib/syskit/remote_state_getter.rb', line 143

def clear
    state_queue.clear
    current_state.reset(nil)
    @last_read_state = nil
end

#connected?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whether the polling thread is alive

Returns:

  • (Boolean)


150
151
152
# File 'lib/syskit/remote_state_getter.rb', line 150

def connected?
    !exit_condition.set?
end

#disconnectObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Stop polling completely

After calling #disconnect, the reader cannot be connected again. Use #pause and #resume to temporarily stop polling



170
171
172
173
174
175
# File 'lib/syskit/remote_state_getter.rb', line 170

def disconnect
    # This order ensures that {#poll_thread} will quit immediately if it
    # was paused
    exit_condition.set
    run_event.set
end

#joinObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Wait for the poll thread to finish after a #disconnect



178
179
180
# File 'lib/syskit/remote_state_getter.rb', line 178

def join
    poll_thread.join
end

#pauseObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Pause polling until #resume or #disconnect are called



155
156
157
# File 'lib/syskit/remote_state_getter.rb', line 155

def pause
    run_event.reset
end

#poll_loopObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

The internal polling loop



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
# File 'lib/syskit/remote_state_getter.rb', line 55

def poll_loop
    # Analysis to avoid deadlocking in {#wait}
    #
    # In the ensure block:
    # - we've had an exception and poll_thread_error is set
    # - the loop quit because exit_condition was set
    #
    # We synchronize the latch (from wait) and we set exit_condition in
    # the synchronization section to ensure that {#wait} can check for
    # exit_condition being set and set the latch only if it is not
    last_state = nil
    while !exit_condition.set?
        if latch = sync_latch.value
            latch.count_down
        end

        time = Time.now
        state = orocos_task.rtt_state
        if (state != last_state) || !current_state.value
            current_state.reset(state)
            state_queue.push(state)
            last_state = state
        end
        if latch
            latch.count_down
            sync_latch.reset(nil)
        end
        spent = (Time.now - time)
        if spent < period
            exit_condition.wait(period - spent)
        end
        run_event.wait
    end

rescue Exception => e
    poll_thread_error.reset(e)
ensure
    poll_thread_exit_sync.synchronize do
        exit_condition.set
        if latch = sync_latch.value
            latch.count_down
            latch.count_down
        end
    end
end

#readObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Read either a new or the last read state



131
132
133
# File 'lib/syskit/remote_state_getter.rb', line 131

def read
    read_new || last_read_state
end

#read_newObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Read a new state change



136
137
138
139
140
141
# File 'lib/syskit/remote_state_getter.rb', line 136

def read_new
    if new_state = state_queue.pop(true)
        @last_read_state = new_state
    end
rescue ThreadError
end

#ready?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whether the state reader has read at least one state

Returns:

  • (Boolean)


126
127
128
# File 'lib/syskit/remote_state_getter.rb', line 126

def ready?
    last_read_state || (state_queue.size > 0)
end

#resumeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Resume polling after a #pause

It is safe to call even if the polling is currently active



162
163
164
# File 'lib/syskit/remote_state_getter.rb', line 162

def resume
    run_event.set
end

#waitObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Wait for the current state to be read and return it



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/syskit/remote_state_getter.rb', line 102

def wait
    latch = poll_thread_exit_sync.synchronize do
        if !run_event.set?
            raise ThreadError, "#{self} is paused, cannot call #wait"
        elsif error = poll_thread_error.value
            raise error, "#{self}'s poll thread quit with #{error.message}, cannot call #wait", (error.backtrace + caller)
        elsif exit_condition.set?
            raise ThreadError, "#{self} is quitting, cannot call #wait"
        end
        sync_latch.reset(latch = Concurrent::CountDownLatch.new(2))
        latch
    end

    latch.wait
    if error = poll_thread_error.value
        raise error, "#{self}'s poll thread quit with #{error.message} during #wait", (error.backtrace + caller)
    elsif exit_condition.set?
        raise ThreadError, "#{self}#disconnect called within #wait"
    else
        current_state.value
    end
end