miner_mover 0.0.0.4 → 0.1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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