miner_mover 0.0.0.4 → 0.1.1.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: 9e2daa3ba8e778e20c25e3a6363683b3163e1141f9438b53be890845acf5440e
4
+ data.tar.gz: b72dc662be9c50caa281e087b3708c72a53aa5e593eeb053b59a0679238d568b
5
5
  SHA512:
6
- metadata.gz: 174fdfc1a8b30977acf4a5934f087c89da67cd12e1d6ea70f863d7d3a93c58d984b63449c4f48a5e2e53fd8f477eb493c79abfa7c805c038474312c1c6b294e5
7
- data.tar.gz: 6322cb259e9e65d5c57a59a3ab4ea20d7d0e1ef7c5414be691faae6ae7fd19bd0bb5007cd3dd9af87624b5081ddb1fa05d676bb63fcb317ebd40e8964e169729
6
+ metadata.gz: 3203db4674a641c4a53ebed0cea4aaa445ef9e120ad582bc1cfcc787541ee53a8677eff1e4936429c4eaa72ef686ef97941a7a929d2d5db4e88a678684005962
7
+ data.tar.gz: e0052a82fa40bcf19b3382ead683087e52f90e6065751a65ff9475aadd141beb0ebe098673acffe48784f90c672aa5990fe2c0e117a3db4bd1782252af7cec91
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
 
@@ -17,11 +18,12 @@ results as well. Ore is gathered at each depth; either a fixed amount or
17
18
  randomized, based on depth. The amount of time spent mining each level is
18
19
  independent and may be randomized.
19
20
 
20
- https://github.com/rickhull/miner_mover/blob/bd76ea400944aab8eab9e3ffcac85d1e28353eff/lib/miner_mover/worker.rb#L85-L99
21
+ https://github.com/rickhull/miner_mover/blob/4f2a62f6d77316e780c7c13248698d4c57bb392e/lib/miner_mover/worker.rb#L96-L104
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
 
@@ -29,7 +31,7 @@ A mover has a batch size, say 10. As the mover accumulates ore over time,
29
31
  once the batch size is reached, the mover delivers the ore to the destination.
30
32
  Larger batches take longer. The delivery time can be randomized.
31
33
 
32
- https://github.com/rickhull/miner_mover/blob/bd76ea400944aab8eab9e3ffcac85d1e28353eff/lib/miner_mover/worker.rb#L135-L162
34
+ https://github.com/rickhull/miner_mover/blob/4f2a62f6d77316e780c7c13248698d4c57bb392e/lib/miner_mover/worker.rb#L152-L183
33
35
 
34
36
  The time and work spent delivering ore can be simulated three ways,
35
37
  configured via `:work_type`
@@ -42,28 +44,76 @@ 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** (CRuby) 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 rake minitest dotcfg fiber_scheduler`
57
+
58
+ ### Clone
59
+
60
+ ```
61
+ git clone https://github.com/rickhull/miner_mover
62
+ cd miner_mover
63
+ ```
64
+
65
+ ### Try rake
66
+
67
+ Try: `rake -T` to see available [Rake tasks](Rakefile)
68
+
69
+ ```
70
+ $ rake -T
71
+
72
+ rake alt_demo # run all demos minus fiber_scheduler / ractor / process
73
+ rake config # run demo/config
74
+ rake default # rake test
75
+ rake demo # run all demos
76
+ rake fiber # run demo/fiber
77
+ rake fiber_scheduler # run demo/fiber_scheduler
78
+ rake process_pipe # run demo/process_pipe
79
+ rake process_socket # run demo/process_socket
80
+ rake ractor # run demo/ractor
81
+ rake serial # run demo/serial
82
+ rake test # Run tests
83
+ rake thread # run demo/thread
84
+ ```
85
+
86
+ Try: `rake test`
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
 
66
- `$ irb -Ilib -rminer_mover/worker`
116
+ `$ irb -I lib`
67
117
 
68
118
  ```
69
119
  irb(main):001:0> include MinerMover
@@ -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,23 @@ 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
389
+ - `IO.pipe` (streaming / bytes / unidirectional)
390
+ * Unix sockets
391
+ - `UNIXSocket.pair :RAW`
392
+ - `UNIXSocket.pair :DGRAM` (datagram / message / "like UDP")
393
+ - `UNIXSocket.pair :STREAM` (streaming / bytes / "like TCP")
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.1.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!