Class: Syskit::RobyApp::UnmanagedProcess

Inherits:
Orocos::ProcessBase
  • Object
show all
Extended by:
Logger::Hierarchy
Includes:
Logger::Hierarchy
Defined in:
lib/syskit/roby_app/unmanaged_process.rb

Overview

A class API-compatible with Orocos::Process that represents tasks that are not started by the Syskit instance

An UnmanagedProcess can basically be in three states, which are reflected by three threads

#monitor_thread is started when all tasks have been resolved in #resolve_all_tasks. It verifies that the tasks are still reachable. The thread returns when it is not the case.

Whether the process is stil in a good state or not should be tested with #dead?. #verify_threads_state verifies that the threads themselves did not terminate with an exception (and throw the exception).

Defined Under Namespace

Classes: TerminateThread

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(process_manager, name, model, host_id: 'unmanaged_process') ⇒ UnmanagedProcess

Creates a new object managing the tasks that represent a single unmanaged process

Parameters:

  • process_manager (nil, #dead_deployment)

    the process manager which created this process. If non-nil, its #dead_deployment method will be called when stop is called

  • name (String)

    the process name

  • model (OroGen::Spec::Deployment)

    the deployment model

  • host_id (String)

    a string identifying the place where the process is expected to be running



78
79
80
81
82
83
84
85
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 78

def initialize(process_manager, name, model, host_id: 'unmanaged_process')
    @process_manager = process_manager
    @deployed_tasks = nil
    @name_service = process_manager.name_service
    @host_id = host_id
    @quitting = Concurrent::Event.new
    super(name, model)
end

Instance Attribute Details

#deployed_tasks{String=>TaskContext} (readonly)

The set of deployed tasks

Returns:

  • ({String=>TaskContext})

    mapping from the deployed task name as defined in model to the actual Orocos::TaskContext



35
36
37
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 35

def deployed_tasks
  @deployed_tasks
end

#host_idString (readonly)

The host on which this process' tasks run

Returns:

  • (String)


40
41
42
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 40

def host_id
  @host_id
end

#monitor_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 monitors the tasks availability

It is spawned the first time #wait_running returns true



67
68
69
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 67

def monitor_thread
  @monitor_thread
end

#name_serviceObject (readonly)

The name service object which should be used to resolve the tasks



60
61
62
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 60

def name_service
  @name_service
end

#pidInteger (readonly)

The PID of the process in which the tasks run

This is always Process.pid as ruby tasks are instanciated inside the ruby process

Returns:

  • (Integer)


57
58
59
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 57

def pid
  @pid
end

#process_manager#dead_deployment? (readonly)

The Syskit::RobyApp::UnmanagedTasksManager object which created this

If non-nil, the object's #dead_deployment will be called when self is stopped

Returns:

  • (#dead_deployment, nil)


29
30
31
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 29

def process_manager
  @process_manager
end

Instance Method Details

#alive?Boolean

True if the process is running. This is an alias for running?

Returns:

  • (Boolean)


197
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 197

def alive?; !dead? end

#dead?Boolean

Returns true if the process died

Returns:

  • (Boolean)


187
188
189
190
191
192
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 187

def dead?
    if monitor_thread
        return !monitor_thread.alive?
    else quitting?
    end
end

#joinObject

Raises:

  • (NotImplementedError)


201
202
203
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 201

def join
    raise NotImplementedError, "UnmanagedProcess#join is not implemented"
end

#kill(wait = false) ⇒ Object

“Kill” this process

It shuts down the tasks that are part of it



157
158
159
160
161
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 157

def kill(wait = false)
    # Announce we're quitting to #monitor_thread. It's used in
    # #dead? directly if there is no monitoring thread
    @quitting.set 
end

#monitor(period: 0.1) ⇒ Object

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.

Implementation of the monitor thread, i.e. the thread that will detect if the deployment disappears

Parameters:

  • period (Float)

    polling period in seconds



169
170
171
172
173
174
175
176
177
178
179
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 169

def monitor(period: 0.1)
    while !quitting?
        deployed_tasks.each_value do |task|
            begin task.ping
            rescue Orocos::ComError
                return
            end
        end
        sleep period
    end
end

#on_localhost?Boolean

Whether the tasks in this process are running on the same machine than the ruby process

This is always true as ruby tasks are instanciated inside the ruby process

Returns:

  • (Boolean)


49
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 49

def on_localhost?; host_id == 'localhost' end

#quitting?Boolean

Whether #kill requested for #monitor_thread to quit

Returns:

  • (Boolean)


182
183
184
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 182

def quitting?
    @quitting.set?
end

#ready?Boolean

Returns true if the tasks have been successfully discovered

Returns:

  • (Boolean)


195
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 195

def ready?; !!deployed_tasks end

#resolve_all_tasks(cache = Hash.new) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 110

def resolve_all_tasks(cache = Hash.new)
    resolved = model.task_activities.map do |t|
        [t.name, (cache[t.name] ||= name_service.get(t.name))]
    end
    @deployed_tasks = Hash[resolved]
    @monitor_thread = Thread.new do
        monitor
    end
    return @deployed_tasks
rescue Orocos::NotFound, Orocos::ComError => e
    if Time.now - @last_warning > 5
        Syskit.warn "waiting for unmanaged task: #{e}"
        @last_warning = Time.now
    end
    nil
end

#running?Boolean

True if the process is running. This is an alias for alive?

Returns:

  • (Boolean)


199
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 199

def running?; alive? end

#spawn(options = Hash.new) ⇒ void

This method returns an undefined value.

“Starts” this process

It spawns a thread that returns once the task got resolved



92
93
94
95
96
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 92

def spawn(options = Hash.new)
    @spawn_start = Time.now
    @last_warning = Time.now
    @deployed_tasks = nil
end

#task(task_name) ⇒ Object

Returns the component object for the given name

Raises:

  • (RuntimeError)

    if the process is not running yet

  • (ArgumentError)

    if the name is not the name of a task on self



132
133
134
135
136
137
138
139
140
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 132

def task(task_name)
    if !deployed_tasks
        raise RuntimeError, "process not running yet"
    elsif task = deployed_tasks[task_name]
        task
    else
        raise ArgumentError, "#{task_name} is not a task of #{self}"
    end
end

#terminate_and_join_thread(thread) ⇒ Object

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.

Helper method to kill a thread



145
146
147
148
149
150
151
152
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 145

def terminate_and_join_thread(thread)
    return if !thread.alive?
    thread.raise TerminateThread
    begin
        thread.join
    rescue TerminateThread
    end
end

#verify_threads_stateObject

Verifies that the monitor thread is alive and well, or that the process either terminated or is not spawned yet

If the monitor thread terminated unexpectedly, it will raise the exception that terminated it, or a RuntimeError if no exceptions have been raised (which should not be possible)



104
105
106
107
108
# File 'lib/syskit/roby_app/unmanaged_process.rb', line 104

def verify_threads_state
    if monitor_thread && !monitor_thread.alive?
        monitor_thread.join
    end
end