Module: Iodine

Defined in:
lib/iodine.rb,
lib/iodine/http.rb,
lib/iodine/version.rb,
lib/iodine/protocol.rb,
lib/rack/handler/iodine.rb,
ext/iodine/iodine_core.c

Overview

Iodine is both a Rack server and a platform for writing evented network services on Ruby.

Here is a sample Echo server using Iodine:

# define the protocol for our service
class EchoProtocol
  @timeout = 10
  # this is just one possible callback with a recyclable buffer
  def on_message buffer
    # write the data we received
    write "echo: #{buffer}"
    # close the connection when the time comes
    close if buffer =~ /^bye[\n\r]/
  end
end
# create the service instance
Iodine.listen 3000, EchoProtocol
# start the service
Iodine.start

Please read the README file for an introduction to Iodine and an overview of it’s API.

Defined Under Namespace

Modules: Base, Protocol, Rack, Websocket

Constant Summary collapse

VERSION =
'0.2.0'.freeze

Class Method Summary collapse

Class Method Details

.countObject



547
# File 'ext/iodine/iodine_core.c', line 547

static VALUE iodine_count(VALUE self) { return ULONG2NUM(server_count(NULL)); }

.listen(port, handler) ⇒ Object

Sets up a listenning socket. Conncetions received at the assigned port will be handled by the assigned handler.

Multiple services (listenning sockets) can be registered before starting the Iodine event loop.



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'ext/iodine/iodine_core.c', line 394

static VALUE iodine_listen_dyn_protocol(VALUE self, VALUE port, VALUE handler) {
  // validate that the handler is a class and include the Iodine::Protocol
  if (TYPE(handler) == T_CLASS) {
    // include the Protocol module
    // // do we neet to check?
    // if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
    rb_include_module(handler, DynamicProtocol);
    rb_extend_object(handler, DynamicProtocolClass);
  } else {
    rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
    return Qnil;
  }
  if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
    rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
  if (TYPE(port) == T_FIXNUM)
    port = rb_funcall2(port, to_s_method_id, 0, NULL);
  // listen
  server_listen(.port = StringValueCStr(port), .udata = (void *)handler,
                .on_open = on_open_dyn_protocol,
                .on_start = on_server_start_for_handler,
                .on_finish = on_server_on_finish_for_handler);
  return self;
}

.processesObject

Get/Set the number of worker processes. A value greater then 1 will cause the Iodine to “fork” any extra worker processes needed.



46
47
48
# File 'lib/iodine.rb', line 46

def self.processes
  @processes
end

.processes=(count) ⇒ Object

Get/Set the number of worker processes. A value greater then 1 will cause the Iodine to “fork” any extra worker processes needed.



51
52
53
# File 'lib/iodine.rb', line 51

def self.processes=(count)
  @processes = count.to_i
end

.runObject

Runs the required block later. The block might run concurrently with the existing code (depending on the amount and availability of worker threads).

Returns the block object. The block will run only while Iodine is running (run will be delayed until Iodine.start is called, unless Iodine’s event loop is active).



471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'ext/iodine/iodine_core.c', line 471

static VALUE iodine_run_async(VALUE self) {
  // requires a block to be passed
  rb_need_block();
  VALUE block = rb_block_proc();
  if (block == Qnil)
    return Qfalse;
  Registry.add(block);
  if (async_run(iodine_run_once, (void *)block)) {
    server_run_after(1, iodine_run_once, (void *)block);
    ;
  }
  return block;
}

.run_after(milliseconds) ⇒ Object

Runs the required block after the specified number of milliseconds have passed. Time is counted only once Iodine started running (using start).

Always returns a copy of the block object.



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'ext/iodine/iodine_core.c', line 491

static VALUE iodine_run_after(VALUE self, VALUE milliseconds) {
  if (TYPE(milliseconds) != T_FIXNUM) {
    rb_raise(rb_eTypeError, "milliseconds must be a number");
    return Qnil;
  }
  size_t milli = FIX2UINT(milliseconds);
  // requires a block to be passed
  rb_need_block();
  VALUE block = rb_block_proc();
  if (block == Qnil)
    return Qfalse;
  Registry.add(block);
  server_run_after(milli, iodine_run_once, (void *)block);
  return block;
}

.run_every(*args) ⇒ Object

Runs the required block after the specified number of milliseconds have passed. Time is counted only once Iodine started running (using start).

Accepts:

milliseconds

the number of milliseconds between event repetitions.

repetitions

the number of event repetitions. Defaults to 0 (never ending).

block

(required) a block is required, as otherwise there is nothing to

perform.

The event will repeat itself until the number of repetitions had been delpeted.

Always returns a copy of the block object.



523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
# File 'ext/iodine/iodine_core.c', line 523

static VALUE iodine_run_every(int argc, VALUE *argv, VALUE self) {
  VALUE milliseconds, repetitions, block;

  rb_scan_args(argc, argv, "11&", &milliseconds, &repetitions, &block);

  if (TYPE(milliseconds) != T_FIXNUM) {
    rb_raise(rb_eTypeError, "milliseconds must be a number.");
    return Qnil;
  }
  if (repetitions != Qnil && TYPE(repetitions) != T_FIXNUM) {
    rb_raise(rb_eTypeError, "repetitions must be a number or `nil`.");
    return Qnil;
  }

  size_t milli = FIX2UINT(milliseconds);
  size_t repeat = (repetitions == Qnil) ? 0 : FIX2UINT(repetitions);
  // requires a block to be passed
  rb_need_block();
  Registry.add(block);
  server_run_every(milli, repeat, iodine_run_always, (void *)block,
                   (void (*)(void *))Registry.remove);
  return block;
}

.startObject

Starts the Iodine event loop. This will hang the thread until an interrupt (‘^C`) signal is received.

Returns the Iodine module.



568
569
570
571
572
573
574
575
576
# File 'ext/iodine/iodine_core.c', line 568

static VALUE iodine_start(VALUE self) {
  if (iodine_http_review() == -1) {
    perror("Iodine couldn't start HTTP service... port busy? ");
    return Qnil;
  }
  rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self, unblck, NULL);

  return self;
}

.threadsObject

Get/Set the number of threads used in the thread pool (a static thread pool). Can be 1 (single working thread, the main thread will sleep) and can be 0 (the main thread will be used as the only active thread).



36
37
38
# File 'lib/iodine.rb', line 36

def self.threads
  @threads
end

.threads=(count) ⇒ Object

Get/Set the number of threads used in the thread pool (a static thread pool). Can be 1 (single working thread, the main thread will sleep) and can be 0 (the main thread will be used as the only active thread).



41
42
43
# File 'lib/iodine.rb', line 41

def self.threads=(count)
  @threads = count.to_i
end

.warmupObject

Runs the warmup sequence. warmup attempts to get every “autoloaded” (lazy loaded) file to load now instead of waiting for “first access”. This allows multi-threaded safety and better memory utilization during forking.

Use warmup when either processes or threads are set to more then 1.



59
60
61
62
63
64
65
66
67
# File 'lib/iodine.rb', line 59

def self.warmup
  # load anything marked with `autoload`, since autoload isn't thread safe nor fork friendly.
  Module.constants.each do |n|
    begin
      Object.const_get(n)
    rescue Exception => _e
    end
  end
end