Skip to content

Commit 2ea38e4

Browse files
committed
100% documentation coverage.
1 parent e8f39e5 commit 2ea38e4

File tree

11 files changed

+95
-15
lines changed

11 files changed

+95
-15
lines changed

lib/async/container.rb

+2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
require_relative "container/controller"
77

8+
# @namespace
89
module Async
10+
# @namespace
911
module Container
1012
end
1113
end

lib/async/container/error.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
module Async
77
module Container
8+
# Represents an error that occured during container execution.
89
class Error < StandardError
910
end
1011

@@ -13,29 +14,35 @@ class Error < StandardError
1314
# Similar to {Interrupt}, but represents `SIGTERM`.
1415
class Terminate < SignalException
1516
SIGTERM = Signal.list["TERM"]
16-
17+
18+
# Create a new terminate error.
1719
def initialize
1820
super(SIGTERM)
1921
end
2022
end
2123

24+
# Similar to {Interrupt}, but represents `SIGHUP`.
2225
class Restart < SignalException
2326
SIGHUP = Signal.list["HUP"]
2427

28+
# Create a new restart error.
2529
def initialize
2630
super(SIGHUP)
2731
end
2832
end
2933

3034
# Represents the error which occured when a container failed to start up correctly.
3135
class SetupError < Error
36+
# Create a new setup error.
37+
#
38+
# @parameter container [Generic] The container that failed.
3239
def initialize(container)
3340
super("Could not create container!")
3441

3542
@container = container
3643
end
3744

38-
# The container that failed.
45+
# @attribute [Generic] The container that failed.
3946
attr :container
4047
end
4148
end

lib/async/container/forked.rb

+23-2
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,34 @@ def self.for(process)
3535
return instance
3636
end
3737

38+
# Initialize the child process instance.
39+
#
40+
# @parameter io [IO] The IO object to use for communication.
3841
def initialize(io)
3942
super
4043

4144
@name = nil
4245
end
4346

47+
# Generate a hash representation of the process.
48+
#
49+
# @returns [Hash] The process as a hash, including `process_id` and `name`.
4450
def as_json(...)
4551
{
4652
process_id: ::Process.pid,
4753
name: @name,
4854
}
4955
end
5056

57+
# Generate a JSON representation of the process.
58+
#
59+
# @returns [String] The process as JSON.
5160
def to_json(...)
5261
as_json.to_json(...)
5362
end
5463

5564
# Set the process title to the specified value.
65+
#
5666
# @parameter value [String] The name of the process.
5767
def name= value
5868
@name = value
@@ -61,14 +71,17 @@ def name= value
6171
::Process.setproctitle(@name.to_s)
6272
end
6373

64-
# The name of the process.
65-
# @returns [String]
74+
# @returns [String] The name of the process.
6675
def name
6776
@name
6877
end
6978

7079
# Replace the current child process with a different one. Forwards arguments and options to {::Process.exec}.
7180
# This method replaces the child process with the new executable, thus this method never returns.
81+
#
82+
# @parameter arguments [Array] The arguments to pass to the new process.
83+
# @parameter ready [Boolean] If true, informs the parent process that the child is ready. Otherwise, the child process will need to use a notification protocol to inform the parent process that it is ready.
84+
# @parameter options [Hash] Additional options to pass to {::Process.exec}.
7285
def exec(*arguments, ready: true, **options)
7386
if ready
7487
self.ready!(status: "(exec)")
@@ -81,6 +94,7 @@ def exec(*arguments, ready: true, **options)
8194
end
8295

8396
# Fork a child process appropriate for a container.
97+
#
8498
# @returns [Process]
8599
def self.fork(**options)
86100
# $stderr.puts fork: caller
@@ -105,6 +119,13 @@ def self.fork(**options)
105119
end
106120
end
107121

122+
# Spawn a child process using {::Process.spawn}.
123+
#
124+
# The child process will need to inform the parent process that it is ready using a notification protocol.
125+
#
126+
# @parameter arguments [Array] The arguments to pass to the new process.
127+
# @parameter name [String] The name of the process.
128+
# @parameter options [Hash] Additional options to pass to {::Process.spawn}.
108129
def self.spawn(*arguments, name: nil, **options)
109130
self.new(name: name) do |process|
110131
Notify::Pipe.new(process.out).before_spawn(arguments, options)

lib/async/container/generic.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ def self.processor_count(env = ENV)
3232

3333
# A base class for implementing containers.
3434
class Generic
35-
def self.run(*arguments, **options, &block)
36-
self.new.run(*arguments, **options, &block)
35+
# Run a new container.
36+
def self.run(...)
37+
self.new.run(...)
3738
end
3839

3940
UNNAMED = "Unnamed"
4041

42+
# Initialize the container.
43+
#
44+
# @parameter options [Hash] Options passed to the {Group} instance.
4145
def initialize(**options)
4246
@group = Group.new(**options)
4347
@running = true

lib/async/container/group.rb

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def initialize(health_check_interval: 1.0)
2525
@queue = nil
2626
end
2727

28+
# @returns [String] A human-readable representation of the group.
2829
def inspect
2930
"#<#{self.class} running=#{@running.size}>"
3031
end

lib/async/container/keyed.rb

+9-6
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,23 @@ module Container
88
# Tracks a key/value pair such that unmarked keys can be identified and cleaned up.
99
# This helps implement persistent processes that start up child processes per directory or configuration file. If those directories and/or configuration files are removed, the child process can then be cleaned up automatically, because those key/value pairs will not be marked when reloading the container.
1010
class Keyed
11+
# Initialize the keyed instance
12+
#
13+
# @parameter key [Object] The key.
14+
# @parameter value [Object] The value.
1115
def initialize(key, value)
1216
@key = key
1317
@value = value
1418
@marked = true
1519
end
1620

17-
# The key. Normally a symbol or a file-system path.
18-
# @attribute [Object]
21+
# @attribute [Object] The key value, normally a symbol or a file-system path.
1922
attr :key
2023

21-
# The value. Normally a child instance of some sort.
22-
# @attribute [Object]
24+
# @attribute [Object] The value, normally a child instance.
2325
attr :value
2426

25-
# Has the instance been marked?
26-
# @returns [Boolean]
27+
# @returns [Boolean] True if the instance has been marked, during reloading the container.
2728
def marked?
2829
@marked
2930
end
@@ -39,6 +40,8 @@ def clear!
3940
end
4041

4142
# Stop the instance if it was not marked.
43+
#
44+
# @returns [Boolean] True if the instance was stopped.
4245
def stop?
4346
unless @marked
4447
@value.stop

lib/async/container/notify/client.rb

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ module Async
77
module Container
88
# Handles the details of several process readiness protocols.
99
module Notify
10+
# Represents a client that can send messages to the parent controller in order to notify it of readiness, status changes, etc.
11+
#
12+
# A process readiness protocol (e.g. `sd_notify`) is a simple protocol for a child process to notify the parent process that it is ready (e.g. to accept connections, to process requests, etc). This can help dependency-based startup systems to start services in the correct order, and to handle failures gracefully.
1013
class Client
1114
# Notify the parent controller that the child has become ready, with a brief status message.
1215
# @parameters message [Hash] Additional details to send with the message.

lib/async/container/notify/log.rb

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
module Async
1010
module Container
1111
module Notify
12+
# Represents a client that uses a local log file to communicate readiness, status changes, etc.
1213
class Log < Client
1314
# The name of the environment variable which contains the path to the notification socket.
1415
NOTIFY_LOG = "NOTIFY_LOG"

lib/async/container/notify/server.rb

+24
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@
1111
module Async
1212
module Container
1313
module Notify
14+
# A simple UDP server that can be used to receive messages from a child process, tracking readiness, status changes, etc.
1415
class Server
1516
MAXIMUM_MESSAGE_SIZE = 4096
1617

18+
# Parse a message, according to the `sd_notify` protocol.
19+
#
20+
# @parameter message [String] The message to parse.
21+
# @returns [Hash] The parsed message.
1722
def self.load(message)
1823
lines = message.split("\n")
1924

@@ -38,41 +43,60 @@ def self.load(message)
3843
return Hash[pairs]
3944
end
4045

46+
# Generate a new unique path for the UNIX socket.
47+
#
48+
# @returns [String] The path for the UNIX socket.
4149
def self.generate_path
4250
File.expand_path(
4351
"async-container-#{::Process.pid}-#{SecureRandom.hex(8)}.ipc",
4452
Dir.tmpdir
4553
)
4654
end
4755

56+
# Open a new server instance with a temporary and unique path.
4857
def self.open(path = self.generate_path)
4958
self.new(path)
5059
end
5160

61+
# Initialize the server with the given path.
62+
#
63+
# @parameter path [String] The path to the UNIX socket.
5264
def initialize(path)
5365
@path = path
5466
end
5567

68+
# @attribute [String] The path to the UNIX socket.
5669
attr :path
5770

71+
# Generate a bound context for receiving messages.
72+
#
73+
# @returns [Context] The bound context.
5874
def bind
5975
Context.new(@path)
6076
end
6177

78+
# A bound context for receiving messages.
6279
class Context
80+
# Initialize the context with the given path.
81+
#
82+
# @parameter path [String] The path to the UNIX socket.
6383
def initialize(path)
6484
@path = path
6585
@bound = Addrinfo.unix(@path, ::Socket::SOCK_DGRAM).bind
6686

6787
@state = {}
6888
end
6989

90+
# Close the bound context.
7091
def close
7192
@bound.close
7293

7394
File.unlink(@path)
7495
end
7596

97+
# Receive a message from the bound context.
98+
#
99+
# @returns [Hash] The parsed message.
76100
def receive
77101
while true
78102
data, _address, _flags, *_controls = @bound.recvmsg(MAXIMUM_MESSAGE_SIZE)

lib/async/container/statistics.rb

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Async
99
module Container
1010
# Tracks various statistics relating to child instances in a container.
1111
class Statistics
12+
# Initialize the statistics all to 0.
1213
def initialize
1314
@spawns = 0
1415
@restarts = 0

lib/async/container/threaded.rb

+16-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ module Async
1111
module Container
1212
# A multi-thread container which uses {Thread.fork}.
1313
class Threaded < Generic
14-
class Kill < Exception
15-
end
16-
1714
# Indicates that this is not a multi-process container.
1815
def self.multiprocess?
1916
false
@@ -52,12 +49,18 @@ def self.for(thread)
5249
return instance
5350
end
5451

52+
# Initialize the child thread instance.
53+
#
54+
# @parameter io [IO] The IO object to use for communication with the parent.
5555
def initialize(io)
5656
@thread = ::Thread.current
5757

5858
super
5959
end
6060

61+
# Generate a hash representation of the thread.
62+
#
63+
# @returns [Hash] The thread as a hash, including `process_id`, `thread_id`, and `name`.
6164
def as_json(...)
6265
{
6366
process_id: ::Process.pid,
@@ -66,6 +69,9 @@ def as_json(...)
6669
}
6770
end
6871

72+
# Generate a JSON representation of the thread.
73+
#
74+
# @returns [String] The thread as JSON.
6975
def to_json(...)
7076
as_json.to_json(...)
7177
end
@@ -101,6 +107,9 @@ def exec(*arguments, ready: true, **options)
101107
end
102108
end
103109

110+
# Start a new child thread and execute the provided block in it.
111+
#
112+
# @parameter options [Hash] Additional options to to the new child instance.
104113
def self.fork(**options)
105114
self.new(**options) do |thread|
106115
::Thread.new do
@@ -113,6 +122,7 @@ def self.fork(**options)
113122
end
114123

115124
# Initialize the thread.
125+
#
116126
# @parameter name [String] The name to use for the child thread.
117127
def initialize(name: nil)
118128
super()
@@ -230,6 +240,9 @@ def success?
230240
@error.nil?
231241
end
232242

243+
# Convert the status to a hash, suitable for serialization.
244+
#
245+
# @returns [Boolean | String] If the status is an error, the error message is returned, otherwise `true`.
233246
def as_json(...)
234247
if @error
235248
@error.inspect

0 commit comments

Comments
 (0)