miner_mover 0.0.0.4 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3292fa885539a3a1c4aca0940e71dfb03ca69aeff10526ec643c6cc86f2829f6
4
- data.tar.gz: 59bab2d1982bf27221b722407fe15a61d25d9e3da3e24ceec31349ef31a154a5
3
+ metadata.gz: 7d6d5e0e85b066dc78e4e6c7b86d751245f1e2b3809ab176f9b69e7047c2d330
4
+ data.tar.gz: cd0f57ea360871fefdb7927eddef3651a277c8e8116d41a2da085dcf02a3ed73
5
5
  SHA512:
6
- metadata.gz: 174fdfc1a8b30977acf4a5934f087c89da67cd12e1d6ea70f863d7d3a93c58d984b63449c4f48a5e2e53fd8f477eb493c79abfa7c805c038474312c1c6b294e5
7
- data.tar.gz: 6322cb259e9e65d5c57a59a3ab4ea20d7d0e1ef7c5414be691faae6ae7fd19bd0bb5007cd3dd9af87624b5081ddb1fa05d676bb63fcb317ebd40e8964e169729
6
+ metadata.gz: 1a8d2759692483f004c0d512a17b2b4ec6a10e6d26b3e66b8785b28a0510cc3eaf7ad029b95f0a11a37b0264dc028e3c65a5cbccffbc64e5446365cdaf9ae2ce
7
+ data.tar.gz: d88946c92b88073fc01f4b57ae0f13ba157ca7a72c85ae6ad405a0569b311e5420689d76014c5cda88c709b2dfbfa2dbed6b090e2c001894fdf4267686cbd87d
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![Test Status](https://github.com/rickhull/miner_mover/actions/workflows/test.yaml/badge.svg)](https://github.com/rickhull/miner_mover/actions/workflows/test.yaml)
2
+ [![Demo Status](https://github.com/rickhull/miner_mover/actions/workflows/demo.yaml/badge.svg)](https://github.com/rickhull/miner_mover/actions/workflows/demo.yaml)
2
3
 
3
4
  # Miner Mover
4
5
 
@@ -20,8 +21,9 @@ independent and may be randomized.
20
21
  https://github.com/rickhull/miner_mover/blob/bd76ea400944aab8eab9e3ffcac85d1e28353eff/lib/miner_mover/worker.rb#L85-L99
21
22
 
22
23
  In this case, miners are rewarded by calculating `fibonacci(depth)`, using
23
- classic, inefficient fibonacci. 10M ore represents `fibonacci(35)`, which
24
- takes around 0.75 seconds on my local VM.
24
+ classic, inefficient fibonacci.
25
+ `fibonacci(35)` yields around 10M ore, while `fibonacci(30)` yields under
26
+ 1M ore.
25
27
 
26
28
  ## Moving
27
29
 
@@ -42,24 +44,72 @@ configured via `:work_type`
42
44
 
43
45
  ## Install
44
46
 
45
- You'll want to use Ruby 3.x to make the most of Fibers.
47
+ You'll want to use **Ruby 3.x** to make the most of Fibers.
46
48
 
47
- Clone the repo, then install dependencies:
49
+ ### Dependencies
48
50
 
49
51
  * rake
50
52
  * minitest
51
- * compsci
52
53
  * dotcfg
53
54
  * fiber_scheduler
54
55
 
55
- `gem install rake minitest compsci dotcfg fiber_scheduler`
56
+ `gem install miner_mover` will take care of the following:
57
+
58
+ `gem install rake minitest dotcfg fiber_scheduler`
59
+
60
+ ### Clone
61
+
62
+ ```
63
+ git clone https://github.com/rickhull/miner_mover
64
+ cd miner_mover
65
+ ```
66
+
67
+ ### Try rake
68
+
69
+ Try: `rake -T` to see available [Rake tasks](Rakefile)
70
+
71
+ ```
72
+ $ rake -T
73
+
74
+ rake alt_demo # run all demos minus fiber_scheduler / ractor / process
75
+ rake config # run demo/config
76
+ rake default # rake test
77
+ rake demo # run all demos
78
+ rake fiber # run demo/fiber
79
+ rake fiber_scheduler # run demo/fiber_scheduler
80
+ rake process_pipe # run demo/process_pipe
81
+ rake process_socket # run demo/process_socket
82
+ rake ractor # run demo/ractor
83
+ rake serial # run demo/serial
84
+ rake test # Run tests
85
+ rake thread # run demo/thread
86
+ ```
87
+
88
+ ## Rake Tasks
89
+
90
+ Included demonstration scripts can be executed via Rake tasks.
91
+ The following order is recommended:
92
+
93
+ * `rake config`
94
+ * `rake serial`
95
+ * `rake fiber`
96
+ * `rake fiber_scheduler`
97
+ * `rake thread`
98
+ * `rake process_pipe`
99
+ * `rake process_socket`
100
+
101
+ Try each task; there will be about 6 seconds worth of many lines of output
102
+ logging. These rake tasks correspond to the scripts within [`demo/`](demo/).
56
103
 
57
104
  ## Satisfy `LOAD_PATH`
58
105
 
59
- Execute scripts and irb sessions from the project root, e.g. `~/miner_mover`.
60
- Use `-I lib` as a flag to `ruby` or `irb` to add e.g. `~/miner_mover/lib`
61
- to `LOAD_PATH` so that `require 'miner_mover'` will work.
62
- This project does not use `require_relative`.
106
+ Rake tasks take care of `LOAD_PATH`, so the following is
107
+ **only necessary when *not* using rake tasks**:
108
+
109
+ * Execute scripts and irb sessions from the project root, e.g. `~/miner_mover`
110
+ * Use `-I lib` as a flag to `ruby` or `irb` to update `LOAD_PATH` so that
111
+ `require 'miner_mover'` will work.
112
+ * This project does not use `require_relative`
63
113
 
64
114
  ## Exploration in `irb`
65
115
 
@@ -105,13 +155,15 @@ multitasking paradigms in Ruby.
105
155
  * [`demo/fiber_scheduler.rb`](demo/fiber_scheduler.rb)
106
156
  * [`demo/thread.rb`](demo/thread.rb)
107
157
  * [`demo/ractor.rb`](demo/ractor.rb)
158
+ * [`demo/process_pipe.rb`](demo/process_pipe.rb)
159
+ * [`demo/process_socket.rb`](demo/process_socket.rb)
108
160
 
109
161
  See [config/example.cfg](config/example.cfg) for configuration.
110
162
  It will be loaded by default.
111
- Note that serial.rb and fiber.rb have no concurrency and cannot use multiple
112
- miners or movers.
163
+ Note that `serial.rb` and `fiber.rb` have no concurrency and cannot use
164
+ multiple miners or movers.
113
165
 
114
- Execute via e.g. `ruby -Ilib demo/ractor.rb`
166
+ Execute via e.g. `ruby -Ilib demo/thread.rb`
115
167
 
116
168
  # Multitasking
117
169
 
@@ -319,3 +371,19 @@ Ractor.receive - returns a message from the current Ractor's incoming port
319
371
  Ractor.yield - current Ractor sends a message on the outgoing port
320
372
  Ractor#take - returns the next outgoing message from a Ractor
321
373
  ```
374
+
375
+ ## Processes
376
+
377
+ There are many ways to create a process in Ruby, some more useful than
378
+ others. My favorites:
379
+
380
+ * `Process.fork` - when called with a block, the block is only executed in the
381
+ child subprocess
382
+ * `Process.spawn` - extensive options, nonblocking, call Process.wait(pid)
383
+ to get the result
384
+ * `Open3.popen3` - for access to STDIN, STDOUT, STDERR
385
+
386
+ ### IPC
387
+
388
+ * Pipes - streaming bytes: `IO.pipe`
389
+ * Unix sockets - messages with datagrams: `Socket.pair(:UNIX, :DGRAM, 0)`
data/Rakefile CHANGED
@@ -5,16 +5,31 @@ Rake::TestTask.new :test do |t|
5
5
  t.warning = true
6
6
  end
7
7
 
8
+ desc "rake test"
8
9
  task default: :test
9
10
 
11
+ Dir['demo/*.rb'].each { |demo_path|
12
+ name = File.basename(demo_path, '.rb')
13
+ desc "run demo/#{name}"
14
+ task(name) { sh "ruby -Ilib #{demo_path}" }
15
+ }
16
+
17
+ # jruby / truffleruby lack fiber_scheduler, Ractor, and Process#fork
18
+ desc "run all demos minus fiber_scheduler / ractor / process"
19
+ task jvm_demo: [:serial, :fiber, :thread]
20
+
21
+ desc "run all demos"
22
+ task demo: [:serial, :fiber, :fiber_scheduler,
23
+ :thread, :ractor, :process_pipe, :process_socket]
24
+
10
25
  begin
11
26
  require 'buildar'
12
27
 
13
- Buildar.new do |b|
28
+ Buildar.new do |b|
14
29
  b.gemspec_file = 'miner_mover.gemspec'
15
30
  b.version_file = 'VERSION'
16
31
  b.use_git = true
17
32
  end
18
33
  rescue LoadError
19
- warn "buildar tasks unavailable"
34
+ # warn "buildar tasks unavailable"
20
35
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0.4
1
+ 0.1.0.1
data/demo/fiber.rb CHANGED
@@ -2,13 +2,13 @@ require 'miner_mover/run'
2
2
 
3
3
  include MinerMover
4
4
 
5
- run = Run.new.cfg_banner!(duration: 1)
6
- run.timer.timestamp!
7
- run.log "Starting"
5
+ run = Run.new.cfg_banner!(duration: 1).start!
6
+ run.timestamp!
7
+ run.log "Starting #{__FILE__}"
8
8
 
9
9
  stop_mining = false
10
10
  Signal.trap("INT") {
11
- run.timer.timestamp!
11
+ run.timestamp!
12
12
  run.log " *** SIGINT *** Stop Mining"
13
13
  stop_mining = true
14
14
  }
@@ -17,20 +17,16 @@ Signal.trap("INT") {
17
17
  miner = Fiber.new(blocking: true) {
18
18
  run.log "MINE Mining operation started [ctrl-c] to stop"
19
19
  m = run.new_miner
20
-
21
20
  ore_mined = 0
22
21
 
23
- # miner waits for the SIGINT signal to quit
24
- while !stop_mining
22
+ while !stop_mining # SIGINT will trigger stop_mining = true
25
23
  ore = m.mine_ore
26
-
27
- # send any ore mined to the mover
28
- Fiber.yield ore if ore > 0
29
24
  ore_mined += ore
25
+ Fiber.yield ore if ore > 0
30
26
 
31
27
  # stop mining after a while
32
28
  if run.time_limit? or run.ore_limit?(ore_mined)
33
- run.timer.timestamp!
29
+ run.timestamp!
34
30
  m.log format("Mining limit reached: %s", Ore.display(ore_mined))
35
31
  stop_mining = true
36
32
  end
@@ -45,16 +41,14 @@ mover = run.new_mover
45
41
  run.log "MOVE Moving operation started"
46
42
  run.log "WAIT Waiting for ore ..."
47
43
 
44
+ # mover pulls from the queue, loads the ore, and moves it
48
45
  loop {
49
- # pick up ore yielded by the miner
50
46
  ore = miner.resume
51
47
  break if ore == :quit
52
-
53
- # load (and possibly move) the ore
54
- mover.load_ore ore if ore > 0
48
+ mover.load_ore ore if ore > 0 # move_batch happens when a batch is full
55
49
  }
56
50
 
57
- # miner has quit; move any remaining ore and quit
51
+ # move any remaining ore and quit
58
52
  mover.move_batch while mover.batch > 0
59
53
  run.log "QUIT #{mover.status}"
60
54
 
@@ -62,4 +56,4 @@ ore_mined = miner.resume
62
56
  ore_moved = mover.ore_moved
63
57
  run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
64
58
  run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
65
- run.timer.timestamp!
59
+ run.timestamp!
@@ -3,13 +3,13 @@ require 'fiber_scheduler'
3
3
 
4
4
  include MinerMover
5
5
 
6
- run = Run.new.cfg_banner!(duration: 1)
7
- run.timer.timestamp!
8
- run.log "Starting"
6
+ run = Run.new.cfg_banner!(duration: 1).start!
7
+ run.timestamp!
8
+ run.log "Starting #{__FILE__}"
9
9
 
10
10
  stop_mining = false
11
11
  Signal.trap("INT") {
12
- run.timer.timestamp!
12
+ run.timestamp!
13
13
  run.log " *** SIGINT *** Stop Mining"
14
14
  stop_mining = true
15
15
  }
@@ -35,20 +35,16 @@ FiberScheduler do
35
35
  Fiber.schedule do
36
36
  m = run.new_miner
37
37
  m.log "MINE Miner #{i} started"
38
-
39
38
  ore_mined = 0
40
39
 
41
- # miner waits for the SIGINT signal to quit
42
- while !stop_mining
40
+ while !stop_mining # SIGINT will trigger stop_mining = true
43
41
  ore = m.mine_ore
44
-
45
- # send any ore mined to the mover
46
- queue.push(ore) if ore > 0
47
42
  ore_mined += ore
43
+ queue.push(ore) if ore > 0
48
44
 
49
45
  # stop mining after a while
50
46
  if run.time_limit? or run.ore_limit?(ore_mined)
51
- run.timer.timestamp!
47
+ run.timestamp!
52
48
  m.log format("Mining limit reached: %s", Ore.display(ore_mined))
53
49
  stop_mining = true
54
50
  end
@@ -73,16 +69,14 @@ FiberScheduler do
73
69
  m = run.new_mover
74
70
  m.log "MOVE Mover #{i} started"
75
71
 
72
+ # movers pull from the queue, load the ore, and move it
76
73
  loop {
77
- # pick up ore from the miner until we get a :quit message
78
74
  ore = queue.pop
79
75
  break if ore == :quit
80
-
81
- # load (and possibly move) the ore
82
- m.load_ore ore if ore > 0
76
+ m.load_ore ore if ore > 0 # move_batch happens when a batch is full
83
77
  }
84
78
 
85
- # miners have quit; move any remaining ore and quit
79
+ # move any remaining ore and quit
86
80
  m.move_batch while m.batch > 0
87
81
  m.log "QUIT #{m.status}"
88
82
 
@@ -100,9 +94,8 @@ FiberScheduler do
100
94
  # tell every mover to quit
101
95
  run.num_movers.times { queue.push(:quit) }
102
96
 
103
- # queue closes once it is empty
104
- # should helpfully cause errors if something is out of sync
105
- queue.close
97
+ # queue only closes once it is empty
98
+ queue.close # should helpfully cause errors if something is out of sync
106
99
  end
107
100
  end
108
101
 
@@ -114,4 +107,4 @@ total_moved += moved.pop until moved.empty?
114
107
 
115
108
  run.log format("MINE %s mined (%i)", Ore.display(total_mined), total_mined)
116
109
  run.log format("MOVE %s moved (%i)", Ore.display(total_moved), total_moved)
117
- run.timer.timestamp!
110
+ run.timestamp!
@@ -3,22 +3,32 @@ require 'thread'
3
3
 
4
4
  include MinerMover
5
5
 
6
- run = Run.new.cfg_banner!(duration: 1)
7
- run.timer.timestamp!
8
- run.log "Starting"
6
+ run = Run.new.cfg_banner!(duration: 1).start!
7
+ run.timestamp!
8
+ run.log "Starting #{__FILE__}"
9
9
 
10
10
  stop_mining = false
11
11
  Signal.trap("INT") {
12
- run.timer.timestamp!
12
+ run.timestamp!
13
13
  run.log " *** SIGINT *** Stop Mining"
14
14
  stop_mining = true
15
15
  }
16
16
 
17
+ pipe_reader, pipe_writer = IO.pipe
18
+
19
+ def pipe_reader.pop
20
+ Ore.decode self.read(Ore::WORD_LENGTH)
21
+ end
22
+
23
+ def pipe_writer.push amt
24
+ self.write(Ore.encode(amt))
25
+ end
26
+
27
+ # the moving operation executes in its own Process
17
28
  mover = Process.fork {
18
29
  run.log "MOVE Moving operation started"
19
-
20
- # mover queue
21
- queue = Thread::Queue.new
30
+ pipe_writer.close # we're only using pipe_reader in this process
31
+ queue = Thread::Queue.new # distribute incoming ore to mover threads
22
32
 
23
33
  # store the mover threads in an array
24
34
  movers = Array.new(run.num_movers) { |i|
@@ -26,16 +36,11 @@ mover = Process.fork {
26
36
  m = run.new_mover
27
37
  m.log "MOVE Mover #{i} started"
28
38
 
39
+ # movers pull from the queue, load the ore, and move it
29
40
  loop {
30
- # a mover picks up ore from the queue
31
- run.debug && m.log("POP ")
32
41
  ore = queue.pop
33
- run.debug && m.log("POPD #{ore}")
34
-
35
42
  break if ore == :quit
36
-
37
- # load (and possibly move) the ore
38
- m.load_ore ore
43
+ m.load_ore ore # move_batch happens when a batch is full
39
44
  }
40
45
 
41
46
  # move any remaining ore and quit
@@ -45,22 +50,25 @@ mover = Process.fork {
45
50
  }
46
51
  }
47
52
 
53
+ # Miners feed this Process with ore
54
+ # Pass the ore into a queue for the movers
55
+ # When the miners say to quit, tell the movers to quit
48
56
  run.log "WAIT Waiting for ore ..."
49
-
50
- # TODO: pipe or unix socket for ore
51
-
52
- TODO = :notyet
53
-
54
57
  loop {
55
- # pull from the pipe / socket
56
- ore = TODO
57
- break if ore == :quit
58
+ ore = pipe_reader.pop
59
+ break if ore == 0 # signal to quit
58
60
  queue.push ore
59
61
  }
62
+
63
+ # tell all the movers to quit and gather their results
64
+ run.num_movers.times { queue.push :quit }
65
+ ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
66
+ run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
60
67
  }
61
68
 
62
- # our mining operation executes in the main Process, here
69
+ # our mining operation executes in the main process, here
63
70
  run.log "MINE Mining operation started [ctrl-c] to stop"
71
+ pipe_reader.close # we're only using pipe_writer in this process
64
72
 
65
73
  # store the miner threads in an array
66
74
  miners = Array.new(run.num_miners) { |i|
@@ -69,22 +77,14 @@ miners = Array.new(run.num_miners) { |i|
69
77
  m.log "MINE Miner #{i} started"
70
78
  ore_mined = 0
71
79
 
72
- # miners wait for the SIGINT signal to quit
73
- while !stop_mining
80
+ while !stop_mining # SIGINT will trigger stop_mining = true
74
81
  ore = m.mine_ore
75
-
76
- # send any ore mined to the mover Ractor
77
- if ore > 0
78
- run.debug && m.log("SEND #{ore}")
79
- mover.send ore
80
- run.debug && m.log("SENT #{ore}")
81
- end
82
-
83
82
  ore_mined += ore
83
+ pipe_writer.push(ore) if ore > 0
84
84
 
85
85
  # stop mining after a while
86
86
  if run.time_limit? or run.ore_limit?(ore_mined)
87
- run.timer.timestamp!
87
+ run.timestamp!
88
88
  m.log format("Mining limit reached: %s", Ore.display(ore_mined))
89
89
  stop_mining = true
90
90
  end
@@ -101,9 +101,8 @@ ore_mined = miners.map { |thr| thr.value }.sum
101
101
  run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
102
102
 
103
103
  # tell mover to quit
104
- mover.send :quit
104
+ pipe_writer.push 0
105
105
 
106
106
  # wait for results
107
- ore_moved = mover.take
108
- run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
109
- run.timer.timestamp!
107
+ Process.wait
108
+ run.timestamp!
@@ -0,0 +1,111 @@
1
+ require 'miner_mover/run'
2
+ require 'thread'
3
+ require 'socket'
4
+
5
+ include MinerMover
6
+
7
+ run = Run.new.cfg_banner!(duration: 1).start!
8
+ run.timestamp!
9
+ run.log "Starting #{__FILE__}"
10
+
11
+ stop_mining = false
12
+ Signal.trap("INT") {
13
+ run.timestamp!
14
+ run.log " *** SIGINT *** Stop Mining"
15
+ stop_mining = true
16
+ }
17
+
18
+ socktype = [:DGRAM, :STREAM, :RAW].sample
19
+ run.log "SOCK #{socktype}"
20
+ csock, psock = UNIXSocket.pair(socktype)
21
+
22
+ def csock.pop
23
+ Ore.decode self.recv(Ore::WORD_LENGTH)
24
+ end
25
+
26
+ def psock.push amt
27
+ self.send(Ore.encode(amt), 0)
28
+ end
29
+
30
+ # the moving operation executes in its own Process
31
+ mover = Process.fork {
32
+ run.log "MOVE Moving operation started"
33
+ psock.close # we're only using csock in this process
34
+ queue = Thread::Queue.new # distribute incoming ore to mover threads
35
+
36
+ # store the mover threads in an array
37
+ movers = Array.new(run.num_movers) { |i|
38
+ Thread.new {
39
+ m = run.new_mover
40
+ m.log "MOVE Mover #{i} started"
41
+
42
+ # movers pull from the queue, load the ore, and move it
43
+ loop {
44
+ ore = queue.pop
45
+ break if ore == :quit
46
+ m.load_ore ore # move_batch happens when a batch is full
47
+ }
48
+
49
+ # move any remaining ore and quit
50
+ m.move_batch while m.batch > 0
51
+ m.log "QUIT #{m.status}"
52
+ m
53
+ }
54
+ }
55
+
56
+ # Miners feed this Process with ore
57
+ # Pass the ore into a queue for the movers
58
+ # When the miners say to quit, tell the movers to quit
59
+ run.log "WAIT Waiting for ore ..."
60
+ loop {
61
+ ore = csock.pop
62
+ break if ore == 0 # signal to quit
63
+ queue.push ore
64
+ }
65
+
66
+ # tell all the movers to quit and gather their results
67
+ run.num_movers.times { queue.push :quit }
68
+ ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
69
+ run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
70
+ }
71
+
72
+ # our mining operation executes in the main process, here
73
+ run.log "MINE Mining operation started [ctrl-c] to stop"
74
+ csock.close # we're only using psock in this process
75
+
76
+ # store the miner threads in an array
77
+ miners = Array.new(run.num_miners) { |i|
78
+ Thread.new {
79
+ m = run.new_miner
80
+ m.log "MINE Miner #{i} started"
81
+ ore_mined = 0
82
+
83
+ while !stop_mining # SIGINT will trigger stop_mining = true
84
+ ore = m.mine_ore
85
+ ore_mined += ore
86
+ psock.push(ore) if ore > 0
87
+
88
+ # stop mining after a while
89
+ if run.time_limit? or run.ore_limit?(ore_mined)
90
+ run.timestamp!
91
+ m.log format("Mining limit reached: %s", Ore.display(ore_mined))
92
+ stop_mining = true
93
+ end
94
+ end
95
+
96
+ m.log format("MINE Miner %i finished after mining %s",
97
+ i, Ore.display(ore_mined))
98
+ ore_mined
99
+ }
100
+ }
101
+
102
+ # wait on all mining threads to stop
103
+ ore_mined = miners.map { |thr| thr.value }.sum
104
+ run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
105
+
106
+ # tell mover to quit
107
+ psock.push 0
108
+
109
+ # wait for results
110
+ Process.wait
111
+ run.timestamp!
data/demo/ractor.rb CHANGED
@@ -3,13 +3,13 @@ require 'thread'
3
3
 
4
4
  include MinerMover
5
5
 
6
- run = Run.new.cfg_banner!(duration: 1)
7
- run.timer.timestamp!
8
- run.log "Starting"
6
+ run = Run.new.cfg_banner!(duration: 1).start!
7
+ run.timestamp!
8
+ run.log "Starting #{__FILE__}"
9
9
 
10
10
  stop_mining = false
11
11
  Signal.trap("INT") {
12
- run.timer.timestamp!
12
+ run.timestamp!
13
13
  run.log " *** SIGINT *** Stop Mining"
14
14
  stop_mining = true
15
15
  }
@@ -27,16 +27,11 @@ mover = Ractor.new(run) { |r|
27
27
  m = r.new_mover
28
28
  m.log "MOVE Mover #{i} started"
29
29
 
30
+ # movers pull from the queue, load the ore, and move it
30
31
  loop {
31
- # a mover picks up ore from the queue
32
- r.debug && m.log("POP ")
33
32
  ore = queue.pop
34
- r.debug && m.log("POPD #{ore}")
35
-
36
33
  break if ore == :quit
37
-
38
- # load (and possibly move) the ore
39
- m.load_ore ore
34
+ m.load_ore ore # move_batch happens when a batch is full
40
35
  }
41
36
 
42
37
  # move any remaining ore and quit
@@ -53,13 +48,8 @@ mover = Ractor.new(run) { |r|
53
48
  loop {
54
49
  # when the Ractor gets ore, push it into the queue
55
50
  ore = Ractor.recv
56
- r.debug && r.log("RECV #{ore}")
57
-
58
51
  break if ore == :quit
59
-
60
- r.debug && r.log("PUSH #{ore}")
61
52
  queue.push ore
62
- r.debug && r.log("PSHD #{ore}")
63
53
  }
64
54
 
65
55
  # tell all the movers to quit and gather their results
@@ -77,22 +67,14 @@ miners = Array.new(run.num_miners) { |i|
77
67
  m.log "MINE Miner #{i} started"
78
68
  ore_mined = 0
79
69
 
80
- # miners wait for the SIGINT signal to quit
81
- while !stop_mining
70
+ while !stop_mining # SIGINT will trigger stop_mining = true
82
71
  ore = m.mine_ore
83
-
84
- # send any ore mined to the mover Ractor
85
- if ore > 0
86
- run.debug && m.log("SEND #{ore}")
87
- mover.send ore
88
- run.debug && m.log("SENT #{ore}")
89
- end
90
-
91
72
  ore_mined += ore
73
+ mover.send ore if ore > 0 # send any ore mined to the mover Ractor
92
74
 
93
75
  # stop mining after a while
94
76
  if run.time_limit? or run.ore_limit?(ore_mined)
95
- run.timer.timestamp!
77
+ run.timestamp!
96
78
  m.log format("Mining limit reached: %s", Ore.display(ore_mined))
97
79
  stop_mining = true
98
80
  end
@@ -114,4 +96,4 @@ mover.send :quit
114
96
  # wait for results
115
97
  ore_moved = mover.take
116
98
  run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
117
- run.timer.timestamp!
99
+ run.timestamp!
data/demo/serial.rb CHANGED
@@ -2,39 +2,32 @@ require 'miner_mover/run'
2
2
 
3
3
  include MinerMover
4
4
 
5
- run = Run.new.cfg_banner!(duration: 1)
6
- run.timer.timestamp!
7
- run.log "Starting"
5
+ run = Run.new.cfg_banner!(duration: 1).start!
6
+ run.timestamp!
7
+ run.log "Starting #{__FILE__}"
8
8
 
9
9
  stop_mining = false
10
10
  Signal.trap("INT") {
11
- run.timer.timestamp!
11
+ run.timestamp!
12
12
  run.log " *** SIGINT *** Stop Mining"
13
13
  stop_mining = true
14
14
  }
15
15
 
16
- # system 'cpulimit', "--pid=#{Process.pid}", '--limit=1', '--background'
17
-
18
16
  miner = run.new_miner
19
17
  run.log "MINE Mining operation initialized [ctrl-c] to stop"
20
18
 
21
19
  mover = run.new_mover
22
20
  run.log "MOVE Moving operation initialized"
23
-
24
21
  ore_mined = 0
25
22
 
26
- # miner waits for the SIGINT signal to quit
27
- while !stop_mining
28
- # mine the ore
23
+ while !stop_mining # SIGINT will trigger stop_mining = true
29
24
  ore = miner.mine_ore
30
25
  ore_mined += ore
31
-
32
- # load (and possibly move) the ore
33
- mover.load_ore ore if ore > 0
26
+ mover.load_ore ore if ore > 0 # move_batch happens when a batch is full
34
27
 
35
28
  # stop mining after a while
36
29
  if run.time_limit? or run.ore_limit?(ore_mined)
37
- run.timer.timestamp!
30
+ run.timestamp!
38
31
  miner.log format("Mining limit reached: %s", Ore.display(ore_mined))
39
32
  stop_mining = true
40
33
  end
@@ -47,4 +40,4 @@ run.log "QUIT #{mover.status}"
47
40
  ore_moved = mover.ore_moved
48
41
  run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
49
42
  run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
50
- run.timer.timestamp!
43
+ run.timestamp!
data/demo/thread.rb CHANGED
@@ -3,13 +3,13 @@ require 'thread'
3
3
 
4
4
  include MinerMover
5
5
 
6
- run = Run.new.cfg_banner!(duration: 1)
7
- run.timer.timestamp!
8
- run.log "Starting"
6
+ run = Run.new.cfg_banner!(duration: 1).start!
7
+ run.timestamp!
8
+ run.log "Starting #{__FILE__}"
9
9
 
10
10
  stop_mining = false
11
11
  Signal.trap("INT") {
12
- run.timer.timestamp!
12
+ run.timestamp!
13
13
  run.log " *** SIGINT *** Stop Mining"
14
14
  stop_mining = true
15
15
  }
@@ -24,16 +24,11 @@ movers = Array.new(run.num_movers) { |i|
24
24
  m = run.new_mover
25
25
  run.log "MOVE Mover #{i} started"
26
26
 
27
+ # movers pull from the queue, load the ore, and move it
27
28
  loop {
28
- # a mover picks up mined ore from the queue
29
- run.debug && m.log("POP ")
30
29
  ore = queue.pop
31
- run.debug && m.log("POPD #{ore}")
32
-
33
30
  break if ore == :quit
34
-
35
- # load (and possibly move) the ore
36
- m.load_ore ore
31
+ m.load_ore ore # moving of ore possibly happens here (on a full batch)
37
32
  }
38
33
 
39
34
  # move any remaining ore and quit
@@ -45,6 +40,7 @@ movers = Array.new(run.num_movers) { |i|
45
40
 
46
41
 
47
42
  run.log "MINE Mining operation started [ctrl-c] to stop"
43
+
48
44
  # store the miner threads in an array
49
45
  miners = Array.new(run.num_miners) { |i|
50
46
  Thread.new {
@@ -55,19 +51,12 @@ miners = Array.new(run.num_miners) { |i|
55
51
  # miners wait for the SIGINT signal to quit
56
52
  while !stop_mining
57
53
  ore = m.mine_ore
58
-
59
- # send any ore mined to the movers
60
- if ore > 0
61
- run.debug && m.log("PUSH #{ore}")
62
- queue.push ore
63
- run.debug && m.log("PSHD #{ore}")
64
- end
65
-
66
54
  ore_mined += ore
55
+ queue.push ore if ore > 0 # send any ore mined to the movers
67
56
 
68
57
  # stop mining after a while
69
58
  if run.time_limit? or run.ore_limit?(ore_mined)
70
- run.timer.timestamp!
59
+ run.timestamp!
71
60
  m.log format("Mining limit reached: %s", Ore.display(ore_mined))
72
61
  stop_mining = true
73
62
  end
@@ -86,6 +75,7 @@ run.log format("MINE %s mined (%i)", Ore.display(ore_mined), ore_mined)
86
75
  # tell all the movers to quit; gather their results
87
76
  run.num_movers.times { queue.push :quit }
88
77
 
78
+ # wait for results
89
79
  ore_moved = movers.map { |thr| thr.value.ore_moved }.sum
90
80
  run.log format("MOVE %s moved (%i)", Ore.display(ore_moved), ore_moved)
91
- run.timer.timestamp!
81
+ run.timestamp!
@@ -14,19 +14,22 @@ module MinerMover
14
14
  time_limit: 5,
15
15
  ore_limit: 100,
16
16
  logging: true,
17
+ debugging: false,
17
18
  }.freeze,
18
19
  miner: {
19
20
  depth: 30,
20
21
  partial_reward: false,
21
22
  variance: 0,
22
23
  logging: true,
24
+ debugging: false,
23
25
  }.freeze,
24
26
  mover: {
25
- batch_size: 10,
26
- rate: 2,
27
+ batch_size: 8,
28
+ rate: 4,
27
29
  work_type: :wait,
28
30
  variance: 0,
29
31
  logging: true,
32
+ debugging: false,
30
33
  }.freeze,
31
34
  }.freeze
32
35
 
@@ -60,7 +63,7 @@ module MinerMover
60
63
  mover = cfg[:mover] || {}
61
64
  main = cfg[:main] || {}
62
65
  else
63
- raise(Error, "couldn't find miner, mover, or main in #{file}")
66
+ miner, mover, main = {}, {}, {}
64
67
  end
65
68
  { miner: DEFAULT[:miner].merge(miner),
66
69
  mover: DEFAULT[:mover].merge(mover),
@@ -1,5 +1,6 @@
1
1
  require 'miner_mover/worker'
2
2
  require 'miner_mover/config'
3
+ require 'miner_mover/timer'
3
4
 
4
5
  module MinerMover
5
6
  class Run
@@ -15,35 +16,47 @@ module MinerMover
15
16
  f
16
17
  end
17
18
 
18
- attr_accessor :debug, :logging
19
+ attr_accessor :debugging, :logging
19
20
  attr_accessor :num_miners, :num_movers
20
21
  attr_accessor :cfg_file, :cfg, :miner, :mover, :timer
21
22
  attr_accessor :time_limit, :ore_limit
22
23
 
23
- def initialize(cfg_file: nil, timer: nil, debug: false)
24
+ def initialize(cfg_file: nil, timer: nil)
24
25
  @cfg_file = self.class.cfg_file(cfg_file)
25
26
  @cfg = Config.process @cfg_file
26
27
  main = @cfg.fetch :main
27
28
  @miner = @cfg.fetch :miner
28
29
  @mover = @cfg.fetch :mover
29
30
 
31
+ @num_miners = main.fetch :num_miners
32
+ @num_movers = main.fetch :num_movers
33
+
30
34
  @time_limit = main.fetch :time_limit
31
35
  @ore_limit = main.fetch :ore_limit
32
36
  @logging = main.fetch :logging
33
- @num_miners = main.fetch :num_miners
34
- @num_movers = main.fetch :num_movers
37
+ @debugging = main.fetch :debugging
35
38
 
36
- @timer = timer || CompSci::Timer.new
37
- @debug = debug
39
+ @timer = timer || Timer.new
38
40
  end
39
41
 
40
42
  def cfg_banner!(duration: 0)
41
- log "USING: #{@cfg_file}"
43
+ MinerMover.puts "USING: #{@cfg_file}"
42
44
  pp @cfg
43
45
  sleep duration if duration > 0
44
46
  self
45
47
  end
46
48
 
49
+ def start!
50
+ @timer.restart
51
+ self
52
+ end
53
+
54
+ def timestamp!
55
+ dash = '-' * 70
56
+ str = [dash, Timer.timestamp, dash].join(MinerMover::LINE_SEP)
57
+ MinerMover.puts str
58
+ end
59
+
47
60
  def new_miner
48
61
  Miner.new(**@miner)
49
62
  end
@@ -61,7 +74,11 @@ module MinerMover
61
74
  end
62
75
 
63
76
  def log msg
64
- @logging and puts(MinerMover.log_fmt(@timer, ' (main) ', msg))
77
+ @logging and MinerMover.log @timer, ' (main) ', msg
78
+ end
79
+
80
+ def debug msg
81
+ @debugging and MinerMover.log @timer, '(debug) ', msg
65
82
  end
66
83
  end
67
84
  end
@@ -0,0 +1,67 @@
1
+ module MinerMover
2
+ class Timer
3
+ SECS_PER_MIN = 60
4
+ MINS_PER_HOUR = 60
5
+ SECS_PER_HOUR = SECS_PER_MIN * MINS_PER_HOUR
6
+
7
+ # returns a float representing seconds since epoch
8
+ if defined? Process::CLOCK_MONOTONIC
9
+ def self.now
10
+ Process.clock_gettime Process::CLOCK_MONOTONIC
11
+ end
12
+ else
13
+ def self.now
14
+ Time.now.to_f
15
+ end
16
+ end
17
+
18
+ def self.since f
19
+ self.now - f
20
+ end
21
+
22
+ def self.elapsed &work
23
+ f = self.now
24
+ return yield, self.since(f)
25
+ end
26
+
27
+ # HH::MM::SS.mmm.uuuuuuuu
28
+ def self.elapsed_display(elapsed_ms, show_micro: false)
29
+ elapsed_s, ms = elapsed_ms.divmod 1000
30
+ ms_only, ms_fraction = ms.round(8).divmod 1
31
+
32
+ h = elapsed_s / SECS_PER_HOUR
33
+ elapsed_s -= h * SECS_PER_HOUR
34
+ m, s = elapsed_s.divmod SECS_PER_MIN
35
+
36
+ hmsms = [[h, m, s].map { |i| i.to_s.rjust(2, '0') }.join(':'),
37
+ ms_only.to_s.rjust(3, '0')]
38
+ hmsms << (ms_fraction * 10 ** 8).round.to_s.ljust(8, '0') if show_micro
39
+ hmsms.join('.')
40
+ end
41
+
42
+ # YYYY-MM-DD HH::MM::SS.mmm
43
+ def self.timestamp(t = Time.now)
44
+ t.strftime "%Y-%m-%d %H:%M:%S.%L"
45
+ end
46
+
47
+ def restart(t = Time.now)
48
+ @start = t
49
+ self
50
+ end
51
+ alias_method :initialize, :restart
52
+
53
+ def elapsed(t = Time.now)
54
+ t - @start
55
+ end
56
+
57
+ def elapsed_ms(t = Time.now)
58
+ elapsed(t) * 1000
59
+ end
60
+
61
+ def elapsed_display(t = Time.now)
62
+ Timer.elapsed_display(elapsed_ms(t))
63
+ end
64
+ alias_method :to_s, :elapsed_display
65
+ alias_method :inspect, :elapsed_display
66
+ end
67
+ end
@@ -1,16 +1,15 @@
1
1
  require 'miner_mover'
2
- require 'compsci/timer'
3
- require 'compsci/fibonacci'
2
+ require 'miner_mover/timer'
4
3
 
5
4
  module MinerMover
6
- def self.work(duration, type = :wait, fib = 30)
5
+ def self.work(duration, type = :wait, fib_target = 30)
7
6
  case type
8
7
  when :wait
9
8
  sleep duration
10
9
  duration
11
10
  when :cpu
12
- t = CompSci::Timer.new
13
- CompSci::Fibonacci.classic(fib) while t.elapsed < duration
11
+ t = Timer.new
12
+ self.fib(fib_target) while t.elapsed < duration
14
13
  t.elapsed
15
14
  when :instant
16
15
  0
@@ -19,14 +18,19 @@ module MinerMover
19
18
  end
20
19
  end
21
20
 
21
+ def self.fib n
22
+ (n < 2) ? n : fib(n-1) + fib(n-2)
23
+ end
24
+
22
25
  class Worker
23
- attr_accessor :variance, :logging
26
+ attr_accessor :variance, :logging, :debugging
24
27
  attr_reader :timer
25
28
 
26
- def initialize(variance: 0, logging: false, timer: nil)
29
+ def initialize(variance: 0, logging: false, debugging: false, timer: nil)
27
30
  @variance = variance
28
31
  @logging = logging
29
- @timer = timer || CompSci::Timer.new
32
+ @debugging = debugging
33
+ @timer = timer || Timer.new
30
34
  end
31
35
 
32
36
  def id
@@ -36,6 +40,7 @@ module MinerMover
36
40
  def state
37
41
  { id: self.id,
38
42
  logging: @logging,
43
+ debugging: @debugging,
39
44
  timer: @timer.elapsed_ms.round,
40
45
  variance: @variance }
41
46
  end
@@ -45,7 +50,11 @@ module MinerMover
45
50
  end
46
51
 
47
52
  def log msg
48
- @logging && puts(MinerMover.log_fmt(@timer, self.id, msg))
53
+ @logging and MinerMover.log @timer, self.id, msg
54
+ end
55
+
56
+ def debug msg
57
+ @debugging and MinerMover.log @timer, self.id, msg
49
58
  end
50
59
 
51
60
  # 4 levels:
@@ -72,10 +81,12 @@ module MinerMover
72
81
  partial_reward: true,
73
82
  variance: 0,
74
83
  logging: false,
84
+ debugging: false,
75
85
  timer: nil)
76
86
  @partial_reward = partial_reward
77
87
  @depth = depth
78
- super(variance: variance, logging: logging, timer: timer)
88
+ super(variance: variance, logging: logging,
89
+ debugging: debugging, timer: timer)
79
90
  end
80
91
 
81
92
  def state
@@ -84,11 +95,11 @@ module MinerMover
84
95
 
85
96
  def mine_ore(depth = @depth)
86
97
  log format("MINE Depth %i", depth)
87
- ores, elapsed = CompSci::Timer.elapsed {
98
+ ores, elapsed = Timer.elapsed {
88
99
  # every new depth is a new mining operation
89
100
  Array.new(depth) { |d|
90
101
  # mine ore by calculating fibonacci for that depth
91
- mined = CompSci::Fibonacci.classic(self.varied(d).round)
102
+ mined = MinerMover.fib self.varied(d).round
92
103
  @partial_reward ? rand(1 + mined) : mined
93
104
  }
94
105
  }
@@ -107,12 +118,14 @@ module MinerMover
107
118
  work_type: :cpu,
108
119
  variance: 0,
109
120
  logging: false,
121
+ debugging: false,
110
122
  timer: nil)
111
123
  @batch_size = batch_size * Ore::BLOCK
112
124
  @rate = rate.to_f * Ore::BLOCK
113
125
  @work_type = work_type
114
126
  @batch, @batches, @ore_moved = 0, 0, 0
115
- super(variance: variance, logging: logging, timer: timer)
127
+ super(variance: variance, logging: logging,
128
+ debugging: debugging, timer: timer)
116
129
  end
117
130
 
118
131
  def state
@@ -149,7 +162,7 @@ module MinerMover
149
162
  duration = self.varied(amt / @rate)
150
163
 
151
164
  log format("MOVE %s (%.2f s)", Ore.display(amt), duration)
152
- _, elapsed = CompSci::Timer.elapsed { self.move(duration) }
165
+ _, elapsed = Timer.elapsed { self.move(duration) }
153
166
  log format("MOVD %s (%.2f s)", Ore.display(amt), elapsed)
154
167
 
155
168
  # accounting
data/lib/miner_mover.rb CHANGED
@@ -1,7 +1,30 @@
1
1
  module MinerMover
2
- # called by Worker instances, available for general use
2
+ LINE_SEP = $/.freeze
3
+
4
+ # $/ is the default line separator: "\n"
5
+ def self.puts(str, io = $stdout, separator = LINE_SEP)
6
+ self.write_nonblock(io, str + separator)
7
+ end
8
+
9
+ def self.write_nonblock(io, str)
10
+ begin
11
+ # nonblocking write attempt; ensure the full string is written
12
+ size = str.bytesize
13
+ num_bytes = io.write_nonblock(str)
14
+ # blocking write if nonblocking write comes up short
15
+ io.write str.byteslice(num_bytes, size - num_bytes) if num_bytes < size
16
+ rescue IO::WaitWritable, Errno::EINTR
17
+ IO.select([], [io]) # wait until writable
18
+ retry
19
+ end
20
+ end
21
+
3
22
  def self.log_fmt(timer, id, msg)
4
- format("%s %s %s", timer.elapsed_display, id, msg)
23
+ format("%s %s %s", timer, id, msg)
24
+ end
25
+
26
+ def self.log(timer, id, msg)
27
+ self.puts self.log_fmt(timer, id, msg)
5
28
  end
6
29
 
7
30
  # i +- 50% at squeeze 0
@@ -18,7 +41,28 @@ module MinerMover
18
41
 
19
42
  # ore is handled in blocks of 1M
20
43
  module Ore
21
- BLOCK = 1_000_000
44
+ BLOCK = 1_000_000 # between fib(30) and fib(31)
45
+ WORD_LENGTH = 4 # bytes
46
+ WORD_MAX = 256 ** WORD_LENGTH # up to 4.3B ore
47
+ HEX_UNPACK = "H#{WORD_LENGTH * 2}" # 4 bytes is 8 hex digits
48
+
49
+ # return 4 bytes, unsigned 32 bit integer, network order
50
+ def self.encode(ore)
51
+ raise "WORD_MAX overflow: #{self.block(ore)}M ore" if ore > WORD_MAX
52
+ [ore].pack('N')
53
+ end
54
+
55
+ # return an integer from the first 4 bytes
56
+ def self.decode(word)
57
+ raise "unexpected size: #{word.bytesize}" unless word.bytesize == 4
58
+ word.unpack('N').first
59
+ end
60
+
61
+ # return "0x01020304" for "\x01\x02\x03\x04"
62
+ def self.hex(word)
63
+ raise "unexpected size: #{word.bytesize}" unless word.bytesize == 4
64
+ "0x" + word.unpack(HEX_UNPACK).first
65
+ end
22
66
 
23
67
  # raw ore in, blocks out
24
68
  def self.block(ore, size = BLOCK)
data/miner_mover.gemspec CHANGED
@@ -18,13 +18,16 @@ EOF
18
18
  s.files += Dir['test/**/*.rb']
19
19
  s.files += Dir['demo/**/*.rb']
20
20
 
21
- s.add_dependency "compsci", "~> 0.3"
22
21
  s.add_dependency "dotcfg", "~> 1.0"
23
- s.add_dependency "fiber_scheduler", "~> 0.13"
24
22
 
25
- s.add_development_dependency "buildar", "~> 3.0"
26
- s.add_development_dependency "minitest", "~> 5.0"
23
+ s.requirements << "For all features and best performance:"
24
+ s.requirements << "gem install rake minitest fiber_scheduler"
25
+ s.requirements << "For dev tools:"
26
+ s.requirements << "gem install buildar flog flay"
27
+
27
28
  s.add_development_dependency "rake", "~> 13.0" # CVE-2020-8130
29
+ s.add_development_dependency "minitest", "~> 5.0"
30
+ s.add_development_dependency "buildar", "~> 3.0"
28
31
  s.add_development_dependency "flog", "~> 0"
29
32
  s.add_development_dependency "flay", "~> 0"
30
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miner_mover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.4
4
+ version: 0.1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -10,20 +10,6 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 1980-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: compsci
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.3'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '0.3'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: dotcfg
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -39,33 +25,19 @@ dependencies:
39
25
  - !ruby/object:Gem::Version
40
26
  version: '1.0'
41
27
  - !ruby/object:Gem::Dependency
42
- name: fiber_scheduler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.13'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.13'
55
- - !ruby/object:Gem::Dependency
56
- name: buildar
28
+ name: rake
57
29
  requirement: !ruby/object:Gem::Requirement
58
30
  requirements:
59
31
  - - "~>"
60
32
  - !ruby/object:Gem::Version
61
- version: '3.0'
33
+ version: '13.0'
62
34
  type: :development
63
35
  prerelease: false
64
36
  version_requirements: !ruby/object:Gem::Requirement
65
37
  requirements:
66
38
  - - "~>"
67
39
  - !ruby/object:Gem::Version
68
- version: '3.0'
40
+ version: '13.0'
69
41
  - !ruby/object:Gem::Dependency
70
42
  name: minitest
71
43
  requirement: !ruby/object:Gem::Requirement
@@ -81,19 +53,19 @@ dependencies:
81
53
  - !ruby/object:Gem::Version
82
54
  version: '5.0'
83
55
  - !ruby/object:Gem::Dependency
84
- name: rake
56
+ name: buildar
85
57
  requirement: !ruby/object:Gem::Requirement
86
58
  requirements:
87
59
  - - "~>"
88
60
  - !ruby/object:Gem::Version
89
- version: '13.0'
61
+ version: '3.0'
90
62
  type: :development
91
63
  prerelease: false
92
64
  version_requirements: !ruby/object:Gem::Requirement
93
65
  requirements:
94
66
  - - "~>"
95
67
  - !ruby/object:Gem::Version
96
- version: '13.0'
68
+ version: '3.0'
97
69
  - !ruby/object:Gem::Dependency
98
70
  name: flog
99
71
  requirement: !ruby/object:Gem::Requirement
@@ -139,13 +111,15 @@ files:
139
111
  - demo/config.rb
140
112
  - demo/fiber.rb
141
113
  - demo/fiber_scheduler.rb
142
- - demo/process.rb
114
+ - demo/process_pipe.rb
115
+ - demo/process_socket.rb
143
116
  - demo/ractor.rb
144
117
  - demo/serial.rb
145
118
  - demo/thread.rb
146
119
  - lib/miner_mover.rb
147
120
  - lib/miner_mover/config.rb
148
121
  - lib/miner_mover/run.rb
122
+ - lib/miner_mover/timer.rb
149
123
  - lib/miner_mover/worker.rb
150
124
  - miner_mover.gemspec
151
125
  - test/miner_mover.rb
@@ -167,7 +141,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
141
  - - ">="
168
142
  - !ruby/object:Gem::Version
169
143
  version: '0'
170
- requirements: []
144
+ requirements:
145
+ - 'For all features and best performance:'
146
+ - gem install rake minitest fiber_scheduler
147
+ - 'For dev tools:'
148
+ - gem install buildar flog flay
171
149
  rubygems_version: 3.3.20
172
150
  signing_key:
173
151
  specification_version: 4