fsevent 0.1 → 0.2

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
  SHA1:
3
- metadata.gz: 190a71cc39238eaa13406a607bd3156a708b9327
4
- data.tar.gz: 312958f08630a7f81721fa2738b3a5375537bdac
3
+ metadata.gz: a29fe94eaba408b87373eb438cb5586bc0d97906
4
+ data.tar.gz: 837f02c21999e8b41e86e22066d0f198111cbb0c
5
5
  SHA512:
6
- metadata.gz: ee4a193a6b9bae8d14f19be365ff123578495774bb2dcff9a27afac8b83e8a87ad55dd63aaab6f7ac76f7749a28fe704d2d34574f59c6baf5f8491a58c9917b9
7
- data.tar.gz: ee1aa94816594f090d13ea360e8ca2095be7829d1655f6030b9a54b68e4d1c4874007f5908f7200dfac2ef3877b71c705d203d645b00d7e5c0d305b6a9b4c1a0
6
+ metadata.gz: 2e163903a427283fa54b05c675a5aa806f872c750e8da21f3e084061efdc3457e552cc5eeda138cef43f2671c04a703cec9b6143e13106c4ccfdc6d9cbfd7981
7
+ data.tar.gz: 902f766db3e5148e76c60bfd25f245bbd738b423a6d15a89545ff98c19a650ee05fdb1479ca6f59ed966a9276682e1189e13c25dfbd3b0f4377852d6525e5300
data/README.md CHANGED
@@ -2,10 +2,21 @@
2
2
 
3
3
  fail safe event driven framework
4
4
 
5
+ # install
6
+
7
+ ```
8
+ gem install fsevent
9
+ ```
10
+
5
11
  # author
6
12
 
7
13
  Tanaka Akira
8
14
 
15
+ # links
16
+
17
+ - https://github.com/fsevent/fsevent
18
+ - http://rubygems.org/gems/fsevent
19
+
9
20
  # license
10
21
  GPLv3 or later
11
22
 
@@ -1,11 +1,13 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'fsevent'
3
- s.version = '0.1'
4
- s.date = '2013-06-23'
3
+ s.version = '0.2'
4
+ s.date = '2013-06-30'
5
5
  s.author = 'Tanaka Akira'
6
6
  s.email = 'tanaka-akira@aist.go.jp'
7
7
  s.license = 'GPL-3.0+'
8
8
  s.required_ruby_version = '>= 1.9.2'
9
+ s.add_runtime_dependency 'depq', '~> 0.6'
10
+ s.add_development_dependency 'test-unit', '~> 2.5'
9
11
  s.files = %w[
10
12
  .gitignore
11
13
  LICENSE
@@ -13,6 +15,7 @@ Gem::Specification.new do |s|
13
15
  fsevent.gemspec
14
16
  lib/fsevent.rb
15
17
  lib/fsevent/abstractdevice.rb
18
+ lib/fsevent/debugdumper.rb
16
19
  lib/fsevent/failsafedevice.rb
17
20
  lib/fsevent/framework.rb
18
21
  lib/fsevent/periodicschedule.rb
@@ -21,6 +24,7 @@ Gem::Specification.new do |s|
21
24
  lib/fsevent/schedulemerger.rb
22
25
  lib/fsevent/simpledevice.rb
23
26
  lib/fsevent/util.rb
27
+ lib/fsevent/watchset.rb
24
28
  sample/repeat.rb
25
29
  sample/repeat2.rb
26
30
  ]
@@ -17,13 +17,18 @@
17
17
 
18
18
  require 'rbconfig'
19
19
  require 'depq'
20
+ require 'pp'
20
21
 
21
22
  class FSEvent
23
+ class FSEventError < StandardError
24
+ end
22
25
  end
23
26
 
24
27
  require 'fsevent/util'
28
+ require 'fsevent/watchset'
25
29
  require 'fsevent/framework'
26
30
  require 'fsevent/abstractdevice'
31
+ require 'fsevent/debugdumper'
27
32
  require 'fsevent/simpledevice'
28
33
  require 'fsevent/processdevice'
29
34
  require 'fsevent/processdevicec'
@@ -26,13 +26,19 @@ class FSEvent::AbstractDevice
26
26
  attr_reader :name, :schedule
27
27
  attr_writer :framework
28
28
 
29
+ def inspect
30
+ "\#<#{self.class}: #{@name}>"
31
+ end
32
+
29
33
  # Called from the framework when this device is registered.
30
34
  def registered
31
35
  # child process calls:
32
- # * @framework.add_watch
33
- # * @framework.define_status
34
- # * @framework.status_changed # possible but needless
35
- # * @framework.set_elapsed_time
36
+ # * add_watch
37
+ # * del_watch # possible but needless
38
+ # * define_status
39
+ # * modify_status # possible but needless
40
+ # * undefine_status # possible but needless
41
+ # * set_elapsed_time
36
42
  end
37
43
 
38
44
  # Called from the framework when this device is unregistered.
@@ -40,25 +46,39 @@ class FSEvent::AbstractDevice
40
46
  end
41
47
 
42
48
  # Called from the framework
43
- def run(watched_status_change)
49
+ def run(watched_status, changed_status)
44
50
  raise NotImplementedError
45
51
  # child process calls:
46
- # * @framework.add_watch # possible but should be rare
47
- # * @framework.define_status # possible but should be rare
48
- # * @framework.status_changed
49
- # * @framework.set_elapsed_time
52
+ # * add_watch # possible but should be rare
53
+ # * del_watch
54
+ # * define_status # possible but should be rare
55
+ # * modify_status
56
+ # * undefine_status
57
+ # * set_elapsed_time
58
+ end
59
+
60
+ def add_watch(watchee_device_name_pat, status_name_pat, reaction = :immediate)
61
+ @framework.add_watch(watchee_device_name_pat, status_name_pat, reaction)
50
62
  end
51
63
 
52
- def add_watch(watchee_device_name, status_name, reaction = :immediate)
53
- @framework.add_watch(watchee_device_name, status_name, reaction)
64
+ def del_watch(watchee_device_name_pat, status_name_pat)
65
+ @framework.del_watch(watchee_device_name_pat, status_name_pat)
54
66
  end
55
67
 
56
68
  def define_status(status_name, value)
57
69
  @framework.define_status(status_name, value)
58
70
  end
59
71
 
60
- def status_changed(status_name, value)
61
- @framework.status_changed(status_name, value)
72
+ def modify_status(status_name, value)
73
+ @framework.modify_status(status_name, value)
74
+ end
75
+
76
+ def undefine_status(status_name)
77
+ @framework.undefine_status(status_name)
78
+ end
79
+
80
+ def register_device(device)
81
+ @framework.register_device(device)
62
82
  end
63
83
 
64
84
  def unregister_device(device_name)
@@ -0,0 +1,33 @@
1
+ # debugdumper.rb --- device for debug dump
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Abstract class for devices
19
+ #
20
+ class FSEvent::DebugDumper < FSEvent::AbstractDevice
21
+ def initialize(device_name="debugdumper")
22
+ super
23
+ end
24
+
25
+ def registered
26
+ add_watch("*", "*")
27
+ end
28
+
29
+ def run(watched_status, changed_status)
30
+ #pp watched_status
31
+ pp changed_status
32
+ end
33
+ end
@@ -18,9 +18,9 @@
18
18
  class FSEvent::FailSafeDevice < FSEvent::AbstractDevice
19
19
  def initialize(device_name, initial_status, *watchee_device_names)
20
20
  super device_name
21
- raise "One devices required at least" if watchee_device_names.empty?
21
+ raise ArgumentError, "One devices required at least" if watchee_device_names.empty?
22
22
  @current_status = {} # status_name -> value
23
- @current_status_list = {} # status_name -> watchee_device_name status_name -> value
23
+ @current_status_list = {} # status_name -> watchee_device_name -> value
24
24
  @status_merger = {}
25
25
  initial_status.each {|k, v, merger|
26
26
  @current_status[k] = v
@@ -61,10 +61,12 @@ class FSEvent::FailSafeDevice < FSEvent::AbstractDevice
61
61
  }
62
62
  end
63
63
 
64
- def run(watched_status_change)
64
+ def run(watched_status, changed_status)
65
65
  updated = {}
66
- watched_status_change.each {|watchee_device_name, h|
67
- h.each {|status_name, value|
66
+ changed_status.each {|watchee_device_name, h|
67
+ h.each {|status_name, time|
68
+ next if /\A_/ =~ status_name
69
+ value = watched_status[watchee_device_name][status_name]
68
70
  unless updated.has_key? status_name
69
71
  updated[status_name] = @current_status_list[status_name]
70
72
  end
@@ -78,7 +80,7 @@ class FSEvent::FailSafeDevice < FSEvent::AbstractDevice
78
80
  new_val = merger.call(cur_val, *values)
79
81
  if cur_val != new_val
80
82
  @current_status[status_name] = new_val
81
- status_changed status_name, new_val
83
+ modify_status status_name, new_val
82
84
  end
83
85
  }
84
86
  end
@@ -20,279 +20,459 @@ class FSEvent
20
20
 
21
21
  def initialize(initial_time=Time.now)
22
22
  @current_time = initial_time
23
+ @current_count = 0
23
24
 
24
25
  @devices = {} # device_name -> device
26
+ @device_last_run_count = {} # device_name -> count
25
27
 
26
- @status = {} # device_name -> status_name -> value
28
+ # special status:
29
+ # _fsevent : _device_registered_DEVICE_NAME => time
30
+ # _fsevent : _device_unregistered_DEVICE_NAME => time
31
+ # DEVICE_NAME : _status_defined_STATUS_NAME => time
32
+ # DEVICE_NAME : _status_undefined_STATUS_NAME => time
33
+ #
34
+ @status_value = { "_fsevent" => {} } # device_name -> status_name -> value
35
+ @status_time = { "_fsevent" => {} } # device_name -> status_name -> time
36
+ @status_count = { "_fsevent" => {} } # device_name -> status_name -> count
27
37
 
28
- # valid values of reaction: :immediate, :immediate_only_at_beginning, :schedule
29
- @watches = nested_hash(3) # watchee_device_name -> status_name -> watcher_device_name -> reaction
30
- @watch_patterns = [] # [watchee_device_name_pat, status_name_pat, watcher_device_name, reaction]
31
- @watched_status_change = nested_hash(3) # watcher_device_name -> watchee_device_name -> status_name -> value
38
+ @watchset = FSEvent::WatchSet.new
39
+
40
+ @clock_proc = nil
32
41
 
33
42
  @q = Depq.new
34
43
  @schedule_locator = {} # device_name -> locator
35
44
  end
36
45
  attr_reader :current_time
46
+ attr_accessor :clock_proc
37
47
 
38
- def register_device(device, register_time=@current_time)
48
+ def register_device(device)
39
49
  device_name = device.name
40
- value = [:register, device_name, device]
41
- @schedule_locator[device_name] = @q.insert value, register_time
50
+ if !valid_device_name_for_write?(device_name)
51
+ raise ArgumentError, "invalid device name: #{device_name.inspect}"
52
+ end
53
+ if !Thread.current[:fsevent_buffer]
54
+ value = [:register_start, device_name, device]
55
+ @schedule_locator[device_name] = @q.insert value, @current_time
56
+ else
57
+ value = [:register_device, device_name, device]
58
+ Thread.current[:fsevent_buffer] << value
59
+ end
42
60
  end
43
61
 
44
62
  def start
45
63
  until @q.empty?
46
64
  loc = @q.delete_min_locator
47
65
  event_type, *args = loc.value
66
+ @clock_proc.call(@current_time, loc.priority) if @clock_proc && @current_time != loc.priority
48
67
  @current_time = loc.priority
68
+ @current_count += 1
49
69
  case event_type
50
- when :register; at_register(loc, *args)
51
- when :wakeup; at_wakeup(loc, *args)
52
- when :sleep; at_sleep(loc, *args)
70
+ when :register_start; at_register_start(loc, *args)
71
+ when :register_end; at_register_end(loc, *args)
72
+ when :run_start; at_run_start(loc, *args)
73
+ when :run_end; at_run_end(loc, *args)
53
74
  else
54
- raise "unexpected event type: #{event_type}"
75
+ raise FSEvent::FSEventError, "unexpected event type: #{event_type}"
55
76
  end
56
77
  end
57
78
  end
58
79
 
59
80
  def wrap_device_action(&block)
60
- Thread.current[:fsevent_device_watch_buffer] = device_watch_buffer = []
61
- Thread.current[:fsevent_device_define_buffer] = device_define_buffer = []
62
- Thread.current[:fsevent_device_changed_buffer] = device_changed_buffer = []
63
- Thread.current[:fsevent_unregister_device_buffer] = unregister_device_buffer = []
81
+ Thread.current[:fsevent_buffer] = buffer = []
64
82
  Thread.current[:fsevent_device_elapsed_time] = nil
65
83
  t1 = Time.now
66
84
  yield
67
85
  t2 = Time.now
68
86
  elapsed = Thread.current[:fsevent_device_elapsed_time] || t2 - t1
69
- return device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer, elapsed
87
+ return buffer, elapsed
70
88
  ensure
71
- Thread.current[:fsevent_device_watch_buffer] = nil
72
- Thread.current[:fsevent_device_define_buffer] = nil
73
- Thread.current[:fsevent_device_changed_buffer] = nil
74
- Thread.current[:fsevent_unregister_device_buffer] = nil
89
+ Thread.current[:fsevent_buffer] = nil
75
90
  Thread.current[:fsevent_device_elapsed_time] = nil
76
91
  end
77
92
  private :wrap_device_action
78
93
 
79
94
  # Called from a device. (mainly from registered().)
80
- def add_watch(watchee_device_name, status_name, reaction = :immediate)
81
- Thread.current[:fsevent_device_watch_buffer] << [:add, watchee_device_name, status_name, reaction]
95
+ def add_watch(watchee_device_name_pat, status_name_pat, reaction = :immediate)
96
+ if !valid_device_name_pat_for_read?(watchee_device_name_pat)
97
+ raise ArgumentError, "invalid device name pattern: #{watchee_device_name_pat.inspect}"
98
+ end
99
+ if !valid_status_name_pat_for_read?(status_name_pat)
100
+ raise ArgumentError, "invalid status name pattern: #{status_name_pat.inspect}"
101
+ end
102
+ Thread.current[:fsevent_buffer] << [:add_watch, watchee_device_name_pat, status_name_pat, reaction]
82
103
  end
83
104
 
84
105
  # Called from a device. (mainly from registered().)
85
- def del_watch(watchee_device_name, status_name)
86
- Thread.current[:fsevent_device_watch_buffer] << [:del, watchee_device_name, status_name, nil]
106
+ def del_watch(watchee_device_name_pat, status_name_pat)
107
+ if !valid_device_name_pat_for_read?(watchee_device_name_pat)
108
+ raise ArgumentError, "invalid device name pattern: #{watchee_device_name_pat.inspect}"
109
+ end
110
+ if !valid_status_name_pat_for_read?(status_name_pat)
111
+ raise ArgumentError, "invalid status name pattern: #{status_name_pat.inspect}"
112
+ end
113
+ Thread.current[:fsevent_buffer] << [:del_watch, watchee_device_name_pat, status_name_pat]
87
114
  end
88
115
 
89
116
  # Called from a device to define the status.
90
117
  def define_status(status_name, value)
91
- Thread.current[:fsevent_device_define_buffer] << [status_name, value]
118
+ if !valid_status_name_for_write?(status_name)
119
+ raise ArgumentError, "invalid status name: #{status_name.inspect}"
120
+ end
121
+ Thread.current[:fsevent_buffer] << [:define_status, status_name, value]
92
122
  end
93
123
 
94
124
  # Called from a device to notify the status.
95
- def status_changed(status_name, value)
96
- Thread.current[:fsevent_device_changed_buffer] << [status_name, value]
125
+ def modify_status(status_name, value)
126
+ if !valid_status_name_for_write?(status_name)
127
+ raise ArgumentError, "invalid status name: #{status_name.inspect}"
128
+ end
129
+ Thread.current[:fsevent_buffer] << [:modify_status, status_name, value]
130
+ end
131
+
132
+ # Called from a device to define the status.
133
+ def undefine_status(status_name)
134
+ if !valid_status_name_for_write?(status_name)
135
+ raise ArgumentError, "invalid status name: #{status_name.inspect}"
136
+ end
137
+ Thread.current[:fsevent_buffer] << [:undefine_status, status_name]
97
138
  end
98
139
 
99
140
  # Called from a device.
100
141
  def unregister_device(device_name)
101
- Thread.current[:fsevent_unregister_device_buffer] << device_name
142
+ if !valid_device_name_for_write?(device_name)
143
+ raise ArgumentError, "invalid device name: #{device_name.inspect}"
144
+ end
145
+ Thread.current[:fsevent_buffer] << [:unregister_device, device_name]
102
146
  end
103
147
 
104
148
  # Called from a device to set the elapsed time.
105
149
  def set_elapsed_time(t)
106
- raise "elapsed time must be positive: #{t}" if t <= 0
150
+ raise ArgumentError, "negative elapsed time given: #{t}" if t < 0
107
151
  Thread.current[:fsevent_device_elapsed_time] = t
108
152
  end
109
153
 
110
- def at_register(loc, device_name, device)
154
+ def at_register_start(loc, device_name, device)
111
155
  if @devices.has_key? device_name
112
- raise "Device already registered: #{device_name}"
156
+ raise ArgumentError, "Device already registered: #{device_name}"
113
157
  end
114
158
 
115
- device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer, elapsed =
116
- wrap_device_action {
159
+ buffer, elapsed = wrap_device_action {
117
160
  device.framework = self
118
161
  device.registered
119
162
  }
120
163
 
164
+ value = [:register_end, device_name, device, @current_count, buffer]
165
+ loc.update value, @current_time + elapsed
166
+ @q.insert_locator loc
167
+ end
168
+ private :at_register_start
169
+
170
+ def at_register_end(loc, device_name, device, register_start_count, buffer)
171
+ if @devices.has_key? device_name
172
+ raise ArgumentError, "Device already registered: #{device_name}"
173
+ end
174
+
121
175
  @devices[device_name] = device
122
- @status[device_name] = {}
176
+ @device_last_run_count[device_name] = register_start_count
177
+ @status_value[device_name] = {}
178
+ @status_time[device_name] = {}
179
+ @status_count[device_name] = {}
123
180
 
124
- value = [:sleep, device_name, device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer]
125
- loc.update value, current_time + elapsed
126
- @q.insert_locator loc
181
+ internal_update_status("_fsevent", @current_time, "_device_registered_#{device_name}", @current_time)
182
+
183
+ at_run_end(loc, device_name, register_start_count, buffer)
127
184
  end
128
- private :at_register
185
+ private :at_register_end
129
186
 
130
- def at_wakeup(loc, device_name)
187
+ def at_run_start(loc, device_name)
131
188
  time = loc.priority
132
189
  device = @devices[device_name]
133
190
 
134
- watched_status_change = @watched_status_change.delete(device_name)
135
- watched_status_change = nonempty_hash(watched_status_change, 2)
191
+ watched_status, changed_status = notifications(device_name, @device_last_run_count[device_name])
136
192
 
137
- device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer, elapsed =
138
- wrap_device_action { device.run(watched_status_change) }
193
+ buffer, elapsed = wrap_device_action { device.run(watched_status, changed_status) }
139
194
 
140
- value = [:sleep, device_name, device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer]
195
+ value = [:run_end, device_name, @current_count, buffer]
141
196
  loc.update value, time + elapsed
142
197
  @q.insert_locator loc
143
198
  end
144
- private :at_wakeup
145
-
146
- def at_sleep(loc, device_name, device_watch_buffer, device_define_buffer, device_changed_buffer, unregister_device_buffer)
147
- sleep_time = loc.priority
148
- update_status(device_name, device_define_buffer, device_changed_buffer)
149
- wakeup_immediate = update_watch(device_name, device_watch_buffer)
150
- notify_status_change(device_name, sleep_time, device_define_buffer, device_changed_buffer)
151
- wakeup_immediate ||= immediate_wakeup?(device_name)
152
- setup_next_schedule(device_name, loc, sleep_time, wakeup_immediate)
153
- unregister_device_internal(unregister_device_buffer)
199
+ private :at_run_start
200
+
201
+ def notifications(watcher_device_name, last_run_count)
202
+ watched_status = {}
203
+ changed_status = {}
204
+ @watchset.watcher_each(watcher_device_name) {|watchee_device_name_pat, status_name_pat, reaction|
205
+ matched_device_name_each(watchee_device_name_pat) {|watchee_device_name|
206
+ watched_status[watchee_device_name] ||= {}
207
+ changed_status[watchee_device_name] ||= {}
208
+ matched_status_name_each(watchee_device_name, status_name_pat) {|status_name|
209
+ if @status_value.has_key?(watchee_device_name) &&
210
+ @status_value[watchee_device_name].has_key?(status_name)
211
+ watched_status[watchee_device_name][status_name] = @status_value[watchee_device_name][status_name]
212
+ end
213
+ if @status_time.has_key?(watchee_device_name) &&
214
+ @status_time[watchee_device_name].has_key?(status_name) &&
215
+ last_run_count <= @status_count[watchee_device_name][status_name]
216
+ changed_status[watchee_device_name][status_name] = @status_time[watchee_device_name][status_name]
217
+ end
218
+ }
219
+ }
220
+ }
221
+ return watched_status, changed_status
154
222
  end
155
- private :at_sleep
223
+ private :notifications
224
+
225
+ def at_run_end(loc, device_name, run_start_count, buffer)
226
+ @device_last_run_count[device_name] = run_start_count
227
+ run_end_time = loc.priority
156
228
 
157
- def update_status(device_name, device_define_buffer, device_changed_buffer)
158
- device_define_buffer.each {|status_name, value|
159
- if @status[device_name].has_key? status_name
160
- raise "device status already defined: #{device_name} #{status_name}"
229
+ wakeup_immediate = false
230
+ unregister_self = false
231
+
232
+ buffer.each {|tag, *rest|
233
+ case tag
234
+ when :define_status
235
+ internal_define_status(device_name, run_end_time, *rest)
236
+ when :modify_status
237
+ internal_modify_status(device_name, run_end_time, *rest)
238
+ when :undefine_status
239
+ internal_undefine_status(device_name, run_end_time, *rest)
240
+ when :add_watch
241
+ wakeup_immediate |= internal_add_watch(device_name, *rest)
242
+ when :del_watch
243
+ internal_del_watch(device_name, *rest)
244
+ when :register_device
245
+ internal_register_device(device_name, *rest)
246
+ when :unregister_device
247
+ unregister_self |= internal_unregister_device(device_name, *rest)
248
+ else
249
+ raise "unexpected tag: #{tag}"
161
250
  end
162
- @status[device_name][status_name] = value
163
251
  }
164
- device_changed_buffer.each {|status_name, value|
165
- unless @status[device_name].has_key? status_name
166
- raise "device status not defined: #{device_name} #{status_name}"
167
- end
168
- @status[device_name][status_name] = value
252
+
253
+ unless unregister_self
254
+ wakeup_immediate ||= immediate_wakeup_self?(device_name, run_start_count)
255
+ setup_next_schedule(device_name, loc, run_end_time, wakeup_immediate)
256
+ end
257
+ end
258
+ private :at_run_end
259
+
260
+ def internal_register_device(device_name, target_device_name, device)
261
+ value = [:register_start, target_device_name, device]
262
+ @schedule_locator[target_device_name] = @q.insert value, @current_time
263
+ end
264
+
265
+ def internal_define_status(device_name, run_end_time, status_name, value)
266
+ internal_define_status2(device_name, run_end_time, status_name, value)
267
+ internal_update_status(device_name, run_end_time, "_status_defined_#{status_name}", run_end_time)
268
+ end
269
+ private :internal_define_status
270
+
271
+ def internal_update_status(device_name, run_end_time, status_name, value)
272
+ if has_status?(device_name, status_name)
273
+ internal_modify_status2(device_name, run_end_time, status_name, value)
274
+ else
275
+ internal_define_status2(device_name, run_end_time, status_name, value)
276
+ end
277
+ end
278
+ private :internal_update_status
279
+
280
+ def has_status?(device_name, status_name)
281
+ @status_value.has_key?(device_name) && @status_value[device_name].has_key?(status_name)
282
+ end
283
+ private :has_status?
284
+
285
+ def internal_define_status2(device_name, run_end_time, status_name, value)
286
+ unless @status_value.has_key? device_name
287
+ raise ArgumentError, "device not defined: #{device_name}"
288
+ end
289
+ if @status_value[device_name].has_key? status_name
290
+ raise ArgumentError, "device status already defined: #{device_name} #{status_name}"
291
+ end
292
+ @status_value[device_name][status_name] = value
293
+ @status_time[device_name][status_name] = @current_time
294
+ @status_count[device_name][status_name] = @current_count
295
+ lookup_watchers(device_name, status_name).each {|watcher_device_name, reaction|
296
+ set_wakeup_if_possible(watcher_device_name, run_end_time) if reaction_immediate_at_beginning? reaction
169
297
  }
170
298
  end
171
- private :update_status
299
+ private :internal_define_status2
172
300
 
173
- def update_watch(device_name, device_watch_buffer)
174
- wakeup_immediate = false
175
- device_watch_buffer.each {|add_or_del, watchee_device_name, status_name, reaction|
176
- case add_or_del
177
- when :add
178
- @watches[watchee_device_name][status_name][device_name] = reaction
179
- if @status.has_key?(watchee_device_name) &&
180
- @status[watchee_device_name].has_key?(status_name)
181
- @watched_status_change[device_name][watchee_device_name][status_name] = @status[watchee_device_name][status_name]
182
- wakeup_immediate ||= reaction_immediate_at_beginning? reaction
183
- end
184
- when :del
185
- @watches[watchee_device_name][status_name].delete device_name
186
- @watched_status_change[device_name][watchee_device_name].delete status_name
187
- else
188
- raise "unexpected add_or_del: #{add_or_del.inspect}"
301
+ def internal_modify_status(device_name, run_end_time, status_name, value)
302
+ internal_modify_status2(device_name, run_end_time, status_name, value)
303
+ end
304
+ private :internal_modify_status
305
+
306
+ def internal_modify_status2(device_name, run_end_time, status_name, value)
307
+ unless @status_value.has_key? device_name
308
+ raise ArgumentError, "device not defined: #{device_name}"
309
+ end
310
+ unless @status_value[device_name].has_key? status_name
311
+ raise ArgumentError, "device status not defined: #{device_name} #{status_name}"
312
+ end
313
+ @status_value[device_name][status_name] = value
314
+ @status_time[device_name][status_name] = @current_time
315
+ @status_count[device_name][status_name] = @current_count
316
+ lookup_watchers(device_name, status_name).each {|watcher_device_name, reaction|
317
+ set_wakeup_if_possible(watcher_device_name, run_end_time) if reaction_immediate_at_subsequent? reaction
318
+ }
319
+ end
320
+ private :internal_modify_status2
321
+
322
+ def internal_undefine_status(device_name, run_end_time, status_name)
323
+ unless @status_value.has_key? device_name
324
+ raise ArgumentError, "device not defined: #{device_name}"
325
+ end
326
+ unless @status_value[device_name].has_key? status_name
327
+ raise ArgumentError, "device status not defined: #{device_name} #{status_name}"
328
+ end
329
+ @status_value[device_name].delete status_name
330
+ @status_time[device_name][status_name] = @current_time
331
+ @status_count[device_name][status_name] = @current_count
332
+ lookup_watchers(device_name, status_name).each {|watcher_device_name, reaction|
333
+ set_wakeup_if_possible(watcher_device_name, run_end_time) if reaction_immediate_at_subsequent? reaction
334
+ }
335
+ internal_update_status(device_name, run_end_time, "_status_undefined_#{status_name}", run_end_time)
336
+ end
337
+ private :internal_update_status
338
+
339
+ def lookup_watchers(watchee_device_name, status_name)
340
+ @watchset.lookup_watchers(watchee_device_name, status_name)
341
+ end
342
+ private :lookup_watchers
343
+
344
+ def internal_add_watch(watcher_device_name, watchee_device_name_pat, status_name_pat, reaction)
345
+ @watchset.add(watchee_device_name_pat, status_name_pat, watcher_device_name, reaction)
346
+ matched_status_each(watchee_device_name_pat, status_name_pat) {|watchee_device_name, status_name|
347
+ if reaction_immediate_at_beginning? reaction
348
+ return true
189
349
  end
190
350
  }
191
- wakeup_immediate
351
+ false
192
352
  end
193
- private :update_watch
353
+ private :internal_add_watch
194
354
 
195
- def notify_status_change(device_name, sleep_time, device_define_buffer, device_changed_buffer)
196
- device_define_buffer.each {|status_name, _|
197
- value = @status[device_name][status_name]
198
- lookup_watchers(device_name, status_name).each {|watcher_device_name, reaction|
199
- @watched_status_change[watcher_device_name][device_name][status_name] = value
200
- set_wakeup_if_possible(watcher_device_name, sleep_time) if reaction_immediate_at_beginning? reaction
355
+ def matched_status_each(watchee_device_name_pat, status_name_pat)
356
+ matched_device_name_each(watchee_device_name_pat) {|watchee_device_name|
357
+ matched_status_name_each(watchee_device_name, status_name_pat) {|status_name|
358
+ yield watchee_device_name, status_name
201
359
  }
202
360
  }
203
- device_changed_buffer.each {|status_name, _|
204
- value = @status[device_name][status_name]
205
- lookup_watchers(device_name, status_name).each {|watcher_device_name, reaction|
206
- @watched_status_change[watcher_device_name][device_name][status_name] = value
207
- set_wakeup_if_possible(watcher_device_name, sleep_time) if reaction_immediate_at_subsequent? reaction
361
+ end
362
+ private :matched_status_each
363
+
364
+ def matched_device_name_each(device_name_pat)
365
+ if /\*\z/ =~ device_name_pat
366
+ prefix = $`
367
+ @status_time.each {|device_name, _h|
368
+ if device_name.start_with? prefix
369
+ yield device_name
370
+ end
208
371
  }
209
- }
372
+ else
373
+ yield device_name_pat
374
+ end
210
375
  end
211
- private :notify_status_change
212
-
213
- def lookup_watchers(watchee_device_name, status_name)
214
- result = []
215
- if @watches.has_key?(watchee_device_name) &&
216
- @watches[watchee_device_name].has_key?(status_name)
217
- @watches[watchee_device_name][status_name].each {|watcher_device_name, reaction|
218
- result << [watcher_device_name, reaction]
376
+ private :matched_device_name_each
377
+
378
+ def matched_status_name_each(device_name, status_name_pat)
379
+ return unless @status_time.has_key? device_name
380
+ status_hash = @status_time[device_name]
381
+ if /\*\z/ =~ status_name_pat
382
+ prefix = $`
383
+ status_hash.each {|status_name, _value|
384
+ if status_name.start_with? prefix
385
+ yield status_name
386
+ end
219
387
  }
388
+ else
389
+ if status_hash.has_key? status_name_pat
390
+ yield status_name_pat
391
+ end
220
392
  end
221
- result
222
393
  end
223
- private :lookup_watchers
394
+ private :matched_status_name_each
395
+
396
+ def internal_del_watch(watcher_device_name, watchee_device_name_pat, status_name_pat)
397
+ @watchset.del(watchee_device_name_pat, status_name_pat, watcher_device_name)
398
+ end
399
+ private :internal_del_watch
224
400
 
225
401
  def set_wakeup_if_possible(device_name, time)
226
402
  loc = @schedule_locator[device_name]
227
403
  if !loc.in_queue?
228
- loc.update [:wakeup, device_name], time
404
+ loc.update [:run_start, device_name], time
229
405
  @q.insert_locator loc
230
406
  return
231
407
  end
232
408
  case event_type = loc.value.first
233
- when :wakeup # The device is sleeping now.
409
+ when :run_start # The device is sleeping now.
234
410
  if time < loc.priority
235
411
  loc.update_priority time
236
412
  end
237
- when :sleep # The device is working now.
238
- # Nothing to do. at_sleep itself checks arrived events at last.
413
+ when :run_end # The device is working now.
414
+ # Nothing to do. at_run_end itself checks arrived events at last.
239
415
  else
240
- raise "unexpected event type: #{event_type}"
416
+ raise FSEvent::FSEventError, "unexpected event type: #{event_type}"
241
417
  end
242
418
  end
243
419
  private :set_wakeup_if_possible
244
420
 
245
- def setup_next_schedule(device_name, loc, sleep_time, wakeup_immediate)
421
+ def setup_next_schedule(device_name, loc, run_end_time, wakeup_immediate)
246
422
  device = @devices[device_name]
247
- wakeup_time = nil
423
+ run_start_time = nil
248
424
  if wakeup_immediate
249
- wakeup_time = sleep_time
250
- elsif wakeup_time = device.schedule.shift
251
- if wakeup_time < sleep_time
252
- wakeup_time = sleep_time
425
+ run_start_time = run_end_time
426
+ elsif run_start_time = device.schedule.shift
427
+ if run_start_time < run_end_time
428
+ run_start_time = run_end_time
253
429
  end
254
- while device.schedule.first && device.schedule.first < sleep_time
430
+ while device.schedule.first && device.schedule.first < run_end_time
255
431
  device.schedule.shift
256
432
  end
257
433
  end
258
- if wakeup_time
259
- value = [:wakeup, device_name]
260
- loc.update value, wakeup_time
434
+ if run_start_time
435
+ value = [:run_start, device_name]
436
+ loc.update value, run_start_time
261
437
  @q.insert_locator loc
262
438
  end
263
439
  end
264
440
  private :setup_next_schedule
265
441
 
266
- def immediate_wakeup?(watcher_device_name)
267
- return false unless @watched_status_change.has_key?(watcher_device_name)
268
- @watched_status_change[watcher_device_name].each {|watchee_device_name, h|
269
- h.each {|status_name, value|
270
- lookup_watchers(watchee_device_name, status_name).each {|watcher_device_name2, reaction|
271
- next if watcher_device_name != watcher_device_name2
272
- return true if reaction_immediate_at_subsequent?(reaction)
442
+ def immediate_wakeup_self?(watcher_device_name, run_start_count)
443
+ @watchset.watcher_each(watcher_device_name) {|watchee_device_name_pat, status_name_pat, reaction|
444
+ if reaction_immediate_at_subsequent?(reaction)
445
+ matched_status_each(watchee_device_name_pat, status_name_pat) {|watchee_device_name, status_name|
446
+ if @status_count.has_key?(watchee_device_name) &&
447
+ @status_count[watchee_device_name].has_key?(status_name) &&
448
+ run_start_count <= @status_count[watchee_device_name][status_name]
449
+ return true
450
+ end
273
451
  }
274
- }
452
+ end
275
453
  }
276
454
  false
277
455
  end
278
- private :immediate_wakeup?
456
+ private :immediate_wakeup_self?
279
457
 
280
- def unregister_device_internal(unregister_device_buffer)
281
- unregister_device_buffer.each {|device_name|
282
- device = @devices.delete device_name
283
- @status.delete device_name
284
- @watches.each {|watchee_device_name, h1|
285
- h1.each {|status_name, h2|
286
- h2.delete device_name
287
- }
458
+ def internal_unregister_device(self_device_name, target_device_name)
459
+ if @status_value.has_key? target_device_name
460
+ @status_value[target_device_name].keys.each {|status_name|
461
+ next if /\A_/ =~ status_name
462
+ internal_undefine_status(target_device_name, @current_time, status_name)
288
463
  }
289
- @watched_status_change.delete device_name
290
- @status.delete device_name
291
- loc = @schedule_locator.fetch(device_name)
292
- device.unregistered
464
+ end
465
+ device = @devices.delete target_device_name
466
+ @status_value.delete target_device_name
467
+ @watchset.delete_watcher(target_device_name)
468
+ loc = @schedule_locator.delete target_device_name
469
+ if loc.in_queue?
293
470
  @q.delete_locator loc
294
- }
471
+ end
472
+ device.unregistered
473
+ internal_update_status("_fsevent", @current_time, "_device_unregistered_#{target_device_name}", @current_time)
474
+ self_device_name == target_device_name
295
475
  end
296
- private :unregister_device_internal
476
+ private :internal_unregister_device
297
477
 
298
478
  end