Class: Libuv::Loop

Inherits:
Object
  • Object
show all
Extended by:
ClassMethods
Includes:
Assertions, Resource
Defined in:
lib/libuv/loop.rb

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

LOOPS =
ThreadSafe::Cache.new

Constants included from Assertions

Assertions::MSG_NO_PROC

Instance Method Summary collapse

Methods included from ClassMethods

create, current, default, new

Methods included from Resource

#check_result, #check_result!, #resolve, #to_ptr

Methods included from Assertions

#assert_block, #assert_boolean, #assert_type

Constructor Details

#initialize(pointer) ⇒ Loop

Initialize a loop using an FFI::Pointer to a libuv loop



44
45
46
47
48
49
50
51
52
53
54
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
# File 'lib/libuv/loop.rb', line 44

def initialize(pointer) # :notnew:
    @pointer = pointer
    @loop = self

    # Create an async call for scheduling work from other threads
    @run_queue = Queue.new
    @queue_proc = proc do
        # ensure we only execute what was required for this tick
        length = @run_queue.length
        length.times do
            begin
                run = @run_queue.pop true  # pop non-block
                run.call
            rescue Exception => e
                @loop.log :error, :next_tick_cb, e
            end
        end
    end
    @process_queue = Async.new(@loop, @queue_proc)

    # Create a next tick timer
    @next_tick = @loop.timer do
        @next_tick_scheduled = false
        @queue_proc.call
    end

    # Create an async call for ending the loop
    @stop_loop = Async.new @loop do
        LOOPS.delete(@reactor_thread)
        @reactor_thread = nil
        @process_queue.close
        @stop_loop.close
        @next_tick.close

        ::Libuv::Ext.stop(@pointer)
    end
end

Instance Method Details

#all(*promises) ⇒ ::Libuv::Q::Promise

Combines multiple promises into a single promise that is resolved when all of the input promises are resolved. (thread safe)

Parameters:

  • *promises (::Libuv::Q::Promise) —

    a number of promises that will be combined into a single promise

Returns:

  • (::Libuv::Q::Promise) —

    Returns a single promise that will be resolved with an array of values, each value corresponding to the promise at the same index in the promises array. If any of the promises is resolved with a rejection, this resulting promise will be resolved with the same rejection.



123
124
125
# File 'lib/libuv/loop.rb', line 123

def all(*promises)
    Q.all(@loop, *promises)
end

#any(*promises) ⇒ ::Libuv::Q::Promise

Combines multiple promises into a single promise that is resolved when any of the input promises are resolved.

Parameters:

  • *promises (::Libuv::Q::Promise) —

    a number of promises that will be combined into a single promise

Returns:



133
134
135
# File 'lib/libuv/loop.rb', line 133

def any(*promises)
    Q.any(@loop, *promises)
end

#async(callback = nil, &block) ⇒ ::Libuv::Async

Get a new Async handle

Returns:



243
244
245
246
247
248
# File 'lib/libuv/loop.rb', line 243

def async(callback = nil, &block)
    callback ||= block
    handle = Async.new(@loop)
    handle.progress callback if callback
    handle
end

#check(callback = nil, &blk) ⇒ ::Libuv::Check

Get a new Check handle

Returns:



228
229
230
# File 'lib/libuv/loop.rb', line 228

def check(callback = nil, &blk)
    Check.new(@loop, callback || blk)
end

#defer ⇒ ::Libuv::Q::Deferred

Creates a deferred result object for where the result of an operation may only be returned at some point in the future or is being processed on a different thread (thread safe)



111
112
113
# File 'lib/libuv/loop.rb', line 111

def defer
    Q.defer(@loop)
end

#file(path, flags = 0, mode = 0) ⇒ ::Libuv::File

Opens a file and returns an object that can be used to manipulate it

Parameters:

  • path (String) —

    the path to the file or folder for watching

  • flags (Integer) (defaults to: 0) —

    see ruby File::Constants

  • mode (Integer) (defaults to: 0)

Returns:



300
301
302
303
304
305
# File 'lib/libuv/loop.rb', line 300

def file(path, flags = 0, mode = 0)
    assert_type(String, path, "path must be a String")
    assert_type(Integer, flags, "flags must be an Integer")
    assert_type(Integer, mode, "mode must be an Integer")
    File.new(@loop, path, flags, mode)
end

#filesystem ⇒ ::Libuv::Filesystem

Returns an object for manipulating the filesystem

Returns:



310
311
312
# File 'lib/libuv/loop.rb', line 310

def filesystem
    Filesystem.new(@loop)
end

#finally(*promises) ⇒ ::Libuv::Q::Promise

Combines multiple promises into a single promise that is resolved when all of the input promises are resolved or rejected.

Parameters:

  • *promises (::Libuv::Q::Promise) —

    a number of promises that will be combined into a single promise

Returns:

  • (::Libuv::Q::Promise) —

    Returns a single promise that will be resolved with an array of values, each [result, wasResolved] value pair corresponding to a at the same index in the promises array.



144
145
146
# File 'lib/libuv/loop.rb', line 144

def finally(*promises)
    Q.finally(@loop, *promises)
end

#fs_event(path) ⇒ ::Libuv::FSEvent

Get a new FSEvent instance

Parameters:

  • path (String) —

    the path to the file or folder for watching

Returns:

Raises:

  • (ArgumentError) —

    if path is not a string



289
290
291
292
# File 'lib/libuv/loop.rb', line 289

def fs_event(path)
    assert_type(String, path)
    FSEvent.new(@loop, path)
end

#handle ⇒ Object



82
# File 'lib/libuv/loop.rb', line 82

def handle; @pointer; end

#idle(callback = nil, &block) ⇒ ::Libuv::Idle

Get a new Idle handle

Parameters:

  • callback (Proc) (defaults to: nil) —

    the callback to be called on idle trigger

Returns:



236
237
238
# File 'lib/libuv/loop.rb', line 236

def idle(callback = nil, &block)
    Idle.new(@loop, callback || block)
end

#log(level, id, *args) ⇒ Object

Notifies the loop there was an event that should be logged

Parameters:

  • level (Symbol) —

    the error level (info, warn, error etc)

  • id (Object) —

    some kind of identifying information

  • *args (*args) —

    any additional information



355
356
357
# File 'lib/libuv/loop.rb', line 355

def log(level, id, *args)
    @loop_notify.notify(level, id, *args)
end

#lookup(hostname, hint = :IPv4, port = 9, &block) ⇒ ::Libuv::Dns

Lookup a hostname

Parameters:

  • hostname (String) —

    the domain name to lookup

  • port (Integer, String) (defaults to: 9) —

    the service being connected too

  • callback (Proc) —

    the callback to be called on success

Returns:



278
279
280
281
282
# File 'lib/libuv/loop.rb', line 278

def lookup(hostname, hint = :IPv4, port = 9, &block)
    dns = Dns.new(@loop, hostname, port, hint)    # Work is a promise object
    dns.then block if block_given?
    dns
end

#lookup_error(err) ⇒ ::Libuv::Error

Lookup an error code and return is as an error object

Parameters:

  • err (Integer) —

    The error code to look up.

Returns:



167
168
169
170
171
172
173
174
175
# File 'lib/libuv/loop.rb', line 167

def lookup_error(err)
    name = ::Libuv::Ext.err_name(err)
    msg  = ::Libuv::Ext.strerror(err)

    ::Libuv::Error.const_get(name.to_sym).new(msg)
rescue Exception => e
    @loop.log :warn, :error_lookup_failed, e
    ::Libuv::Error::UNKNOWN.new("error lookup failed for code #{err} #{name} #{msg}")
end

#next_tick(callback = nil, &block) ⇒ Object

Queue some work to be processed in the next iteration of the event loop (thread safe)

Parameters:

  • callback (Proc) (defaults to: nil) —

    the callback to be called on the reactor thread

Raises:

  • (ArgumentError) —

    if block is not given



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/libuv/loop.rb', line 334

def next_tick(callback = nil, &block)
    callback ||= block
    assert_block(callback)

    @run_queue << callback
    if reactor_thread?
        # Create a next tick timer
        if not @next_tick_scheduled
            @next_tick.start(0)
            @next_tick_scheduled = true
        end
    else
        @process_queue.call
    end
end

#now ⇒ Fixnum

Get current time in milliseconds

Returns:

  • (Fixnum)


159
160
161
# File 'lib/libuv/loop.rb', line 159

def now
    ::Libuv::Ext.now(@pointer)
end

#pipe(ipc = false) ⇒ ::Libuv::Pipe

Get a new Pipe instance

Parameters:

  • ipc (true, false) (defaults to: false) —

    indicate if a handle will be used for ipc, useful for sharing tcp socket between processes

Returns:



206
207
208
# File 'lib/libuv/loop.rb', line 206

def pipe(ipc = false)
    Pipe.new(@loop, ipc)
end

#prepare(callback = nil, &blk) ⇒ ::Libuv::Prepare

Get a new Prepare handle

Returns:



221
222
223
# File 'lib/libuv/loop.rb', line 221

def prepare(callback = nil, &blk)
    Prepare.new(@loop, callback || blk)
end

#reactor_running? ⇒ Boolean

Tells you whether the Libuv reactor loop is currently running.

Returns:

  • (Boolean)


381
382
383
# File 'lib/libuv/loop.rb', line 381

def reactor_running?
    !@reactor_thread.nil?
end

#reactor_thread ⇒ Thread

Exposed to allow joining on the thread, when run in a multithreaded environment. Performing other actions on the thread has undefined semantics (read: a dangerous endevor).

Returns:

  • (Thread)


374
375
376
# File 'lib/libuv/loop.rb', line 374

def reactor_thread
    @reactor_thread
end

#reactor_thread? ⇒ Boolean

True if the calling thread is the same thread as the reactor.

Returns:

  • (Boolean)


367
368
369
# File 'lib/libuv/loop.rb', line 367

def reactor_thread?
    @reactor_thread == Thread.current
end

#run(run_type = :UV_RUN_DEFAULT) {|promise| ... } ⇒ Object

Run the actual event loop. This method will block until the loop is stopped.

Parameters:

  • run_type (:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT) (defaults to: :UV_RUN_DEFAULT)

Yield Parameters:

  • promise (::Libuv::Q::Promise) —

    Yields a promise that can be used for logging unhandled exceptions on the loop.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/libuv/loop.rb', line 89

def run(run_type = :UV_RUN_DEFAULT)
    if @reactor_thread.nil?
        @loop_notify = @loop.defer

        begin
            @reactor_thread = Thread.current
            LOOPS[@reactor_thread] = @loop
            yield  @loop_notify.promise if block_given?
            ::Libuv::Ext.run(@pointer, run_type)  # This is blocking
        ensure
            @reactor_thread = nil
            @run_queue.clear
        end
    end
    @loop
end

#schedule(callback = nil, &block) ⇒ Object

Schedule some work to be processed on the event loop as soon as possible (thread safe)

Parameters:

  • callback (Proc) (defaults to: nil) —

    the callback to be called on the reactor thread

Raises:

  • (ArgumentError) —

    if block is not given



318
319
320
321
322
323
324
325
326
327
328
# File 'lib/libuv/loop.rb', line 318

def schedule(callback = nil, &block)
    callback ||= block
    assert_block(callback)

    if reactor_thread?
        block.call
    else
        @run_queue << callback
        @process_queue.call
    end
end

#signal(signum = nil, callback = nil, &block) ⇒ ::Libuv::Signal

Get a new signal handler

Returns:



253
254
255
256
257
258
259
# File 'lib/libuv/loop.rb', line 253

def signal(signum = nil, callback = nil, &block)
    callback ||= block
    handle = Signal.new(@loop)
    handle.progress callback if callback
    handle.start(signum) if signum
    handle
end

#stop ⇒ Object

Closes handles opened by the loop class and completes the current loop iteration (thread safe)



360
361
362
# File 'lib/libuv/loop.rb', line 360

def stop
    @stop_loop.call
end

#tcp ⇒ ::Libuv::TCP

Get a new TCP instance

Returns:



180
181
182
# File 'lib/libuv/loop.rb', line 180

def tcp
    TCP.new(@loop)
end

#timer(callback = nil, &blk) ⇒ ::Libuv::Timer

Get a new timer instance

Parameters:

  • callback (Proc) (defaults to: nil) —

    the callback to be called on timer trigger

Returns:



214
215
216
# File 'lib/libuv/loop.rb', line 214

def timer(callback = nil, &blk)
    Timer.new(@loop, callback || blk)
end

#tty(fileno, readable = false) ⇒ ::Libuv::TTY

Get a new TTY instance

Parameters:

  • fileno (Integer) —

    Integer file descriptor of a tty device

  • readable (true, false) (defaults to: false) —

    Boolean indicating if TTY is readable

Returns:



196
197
198
199
200
# File 'lib/libuv/loop.rb', line 196

def tty(fileno, readable = false)
    assert_type(Integer, fileno, "io#fileno must return an integer file descriptor, #{fileno.inspect} given")

    TTY.new(@loop, fileno, readable)
end

#udp ⇒ ::Libuv::UDP

Get a new UDP instance

Returns:



187
188
189
# File 'lib/libuv/loop.rb', line 187

def udp
    UDP.new(@loop)
end

#update_time ⇒ Object

forces loop time update, useful for getting more granular times

Returns:

  • nil



152
153
154
# File 'lib/libuv/loop.rb', line 152

def update_time
    ::Libuv::Ext.update_time(@pointer)
end

#work(callback = nil, &block) ⇒ ::Libuv::Work

Queue some work for processing in the libuv thread pool

Parameters:

  • callback (Proc) (defaults to: nil) —

    the callback to be called in the thread pool

Returns:

Raises:

  • (ArgumentError) —

    if block is not given



266
267
268
269
270
# File 'lib/libuv/loop.rb', line 266

def work(callback = nil, &block)
    callback ||= block
    assert_block(callback)
    Work.new(@loop, callback)    # Work is a promise object
end