flok 0.0.82 → 0.0.83

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
  SHA1:
3
- metadata.gz: ebf963c82063ec89074d43114c4416bcc3505df8
4
- data.tar.gz: ece62f398cf5e92ce976597f1fee070678bf13d4
3
+ metadata.gz: 7bd8fa8fdb50bddf42da16826b0046fb59193ce7
4
+ data.tar.gz: f65050b61273c66c424aeae68568cf0eb7272512
5
5
  SHA512:
6
- metadata.gz: 5b3367ebb243879208147f3b944558fb880e9774c00338e390b0d947cc41ca088a1638ca3db3915deab288bc2477b7955ad39a8095e66ce4942c71f54a2a98e1
7
- data.tar.gz: e85b5607da669fcac1a3e795541ca7b37cf9f59295c187989bd367b2db26d2701dfcf69b28bbc22c82ffad4114d5e87c95f6ec552da50c5b48f489d7e66a9841
6
+ metadata.gz: d70b9cdc7e87a2b068b55b255a112838a04bac27dca48d5d05b1d6e5023d71a05930856dfe3a256161f4b557a1fbb13996cd7d561c7b45808882256d39057c34
7
+ data.tar.gz: 36c1164fdefca9a1f9a53b9e845d4cdebfd66f1ae8084bfa0f3d4433a8bcf834003694d71453fb81d46a55ffe73b95c919c5799652b0d14de3d2bf01b40a1f58
@@ -2,10 +2,14 @@
2
2
  <% [0].each do |i| %>
3
3
  //Destination for events sent from the sockio driver
4
4
  function __pg_sockio<%= i %>_xevent_handler(ep, ename, einfo) {
5
+ //Events
6
+ //update - From the sockio interface, an update refers to a page update
7
+ //interval - Every 15 seconds, from the callout timer.
5
8
  if (ename === "update") {
6
9
  //If changes_id was given
7
10
  if (einfo.changes_id !== undefined) {
8
- vm_mark_changes_synced(vm_cache[pg_sockio<%= i %>_ns][einfo.page._id], einfo.changes_id)
11
+ //This is a friendly function that will ignore mis-matches of changes_id
12
+ vm_mark_changes_synced(vm_cache[pg_sockio<%= i %>_ns][einfo.page._id], einfo.changes_id);
9
13
  }
10
14
 
11
15
  //If page exists, then we need to rebase the page, this will actually
@@ -15,10 +19,39 @@
15
19
  vm_rebase(vm_cache[pg_sockio<%= i %>_ns][einfo.page._id], einfo.page);
16
20
  }
17
21
 
22
+ //Mark page as synced if it contains no changes, we don't need to check base
23
+ //here because if it has base, it has __changes as well
24
+ if (einfo.page.__changes === undefined) {
25
+ vm_pg_unmark_needs_sync(pg_sockio<%= i %>_ns, einfo.page._id)
26
+ }
27
+
18
28
  //Write out page
19
29
  vm_transaction_begin();
20
30
  vm_cache_write(pg_sockio<%= i %>_ns, einfo.page);
21
31
  vm_transaction_end();
32
+ } else if (ename === "interval") {
33
+
34
+ //Create watch_list
35
+ var watch_list = [];
36
+ var watched_keys = Object.keys(vm_notify_map[pg_sockio<%= i %>_ns]);
37
+ for (var i = 0; i < watched_keys.length; ++i) {
38
+ var key = watched_keys[i];
39
+ watch_list.push(key);
40
+
41
+ //If entry exists, put in the hash value
42
+ if (vm_cache[pg_sockio<%= i %>_ns][key] === undefined) {
43
+ watch_list.push(null);
44
+ } else {
45
+ watch_list.push(vm_cache[pg_sockio<%= i %>_ns][key]._hash);
46
+ }
47
+ }
48
+
49
+ //Synchronize the watchlist with the server
50
+ var resync_info = {
51
+ watch_list: watch_list
52
+ };
53
+
54
+ SEND("net", "if_sockio_send", pg_sockio<%= i %>_bp, "resync", resync_info);
22
55
  } else {
23
56
  <% if @debug %>
24
57
  throw "pg_sockio<%= i %>_xevent_handler received an event called: " + ename + "that it does not know how to handle. This event should never have even been forwarded, but you may have missed adding the handler code if you did request a forward"
@@ -46,6 +79,9 @@
46
79
  //Signal that the socket.io driver should forward all events to the socket defined by pg_sockio{N}_bp
47
80
  //to the endpoint (with the same reference)
48
81
  SEND("net", "if_sockio_fwd", pg_sockio<%= i %>_bp, "update", pg_sockio<%= i %>_bp);
82
+
83
+ //Request a timer every 15 seconds
84
+ reg_interval(pg_sockio<%= i %>_bp, "interval", 15*4);
49
85
  }
50
86
 
51
87
  function pg_sockio<%= i %>_watch(id, page) {
@@ -56,6 +92,10 @@
56
92
  }
57
93
 
58
94
  function pg_sockio<%= i %>_unwatch(id) {
95
+ var info = {
96
+ page_id: id
97
+ }
98
+ SEND("net", "if_sockio_send", pg_sockio<%= i %>_bp, "unwatch", info);
59
99
  }
60
100
 
61
101
  function pg_sockio<%= i %>_write(page) {
@@ -70,6 +110,12 @@
70
110
  vm_cache_write(pg_sockio<%= i %>_ns, page);
71
111
  vm_transaction_end();
72
112
 
113
+ //Mark pages as needing a synchronization
114
+ vm_pg_mark_needs_sync(pg_sockio<%= i %>_ns, page._id);
115
+ }
116
+
117
+ function pg_sockio<%= i %>_sync(page_id) {
118
+ var page = vm_cache[pg_sockio<%= i %>_ns][page_id];
73
119
  //Clone page and send a copy to the server
74
120
  var copied = vm_copy_page(page);
75
121
  var info = {page: copied, changes: page.__changes, changes_id: page.__changes_id};
@@ -793,6 +793,9 @@ service :vm do
793
793
  }
794
794
 
795
795
  vm_notify_map[params.ns][params.id].splice(midx, 1);
796
+
797
+ //Remove from notify map if no entries exist
798
+ if (vm_notify_map[params.ns][params.id].length === 0) { delete vm_notify_map[params.ns][params.id]; }
796
799
 
797
800
  delete vm_bp_to_nmap[bp][params.ns][params.id];
798
801
 
data/docs/kernel_api.md CHANGED
@@ -39,6 +39,9 @@ instead.
39
39
  0 is the master root view. Returns base pointer. `event_gw` is a pointer to a `vc`. If it is null, then any events coming in will not be sent to
40
40
  somewhere else if they do not match any 'on' for the current action.
41
41
 
42
+ ##Timers
43
+ * See [./callout.md](Callout) for how to register a timer
44
+
42
45
  ##Things that are compiled into the kernel from user given data
43
46
  `MODS` - A list of modules that was specified in `./app/drivers/$PLATFORM/config.yml`
44
47
  `PLATFORM` - The platform that this kernel was compiled with
data/docs/mod/persist.md CHANGED
@@ -11,7 +11,7 @@ Persistance management. Loosely based on redis.
11
11
  `if_per_set_f(ns, key, tp)` - Tell the driver to dereference the telepointer and to save it to disk.
12
12
 
13
13
  For race conditions, e.g, an asynchronous set is followed by a synchronous get, it is undefined as to what that behavior that will be.
14
- It is expected that the kernel should manage the write-back cache and that the driver should not attempt a write back cache unless
14
+ I If the page does not exist, the hash value is null.t is expected that the kernel should manage the write-back cache and that the driver should not attempt a write back cache unless
15
15
  it is convenient to do so.
16
16
 
17
17
  ###Kernel interrupts
data/docs/services/vm.md CHANGED
@@ -222,6 +222,15 @@ The pager synchronization daemon is embodied in the function called `vm_pg_sync_
222
222
  `__changes_id` of the page matches `changes_id`. If the page is based (implying the base page has changes and the page has changes as all base
223
223
  pages have changes), then if the `changes_id` matches the **base** `__changes_id` , the `__base` is removed from the page. If `changes_id`
224
224
  does not match in either of the cases, then nothing happends. This may happend if a synchronization errousouly comes in.
225
+ * **Why do we have both `vm_rebase` and `vm_mark_changes_synced`?**
226
+ * They are used under similar circumstances. You always `vm_mark_changes_synced` before calling `vm_rebase` on a page. The reasoning is that
227
+ `vm_rebase` will assume that the cached page does not contain changes if they are present in `older`. If you didn't do this, then the
228
+ cached page would be rebased and could contain changes even though it's already been rebased on an older page. E.g. `newer[changes, nobase]`
229
+ rebased would be `older[changes, nobase]` where `changes` are equal on the `newer` and `older` but clearly that's incorrect. Another way of
230
+ looking at it is that `vm_rebase` is saying that you are rebasing **on an authority** page and therefore needs no evidence that the page was
231
+ an authority (which is why the `changes_id` can be stripped). Method 3 of looking at it is that `vm_rebase` on a `newer[changes,
232
+ based[changes, nobase]]` with `older` where `older` contains the changes of `newer.__base.__changes`, would result in `older` having
233
+ `newer.__base.__changes` fast-forwarded over it, which it would already contain those changes.
225
234
  ###Non functional (functional as is in lambda calculus, or lisp (no **global** state changes but may modify parameters)
226
235
  ####Pager specific
227
236
  * `vm_cache_write(ns, page)` - Save a page to cache memory. This will not recalculate the page hash. The page will be stored in `vm_cache[ns][id]` by.
@@ -229,7 +238,8 @@ The pager synchronization daemon is embodied in the function called `vm_pg_sync_
229
238
  * The page_id is added to the `vm_unsynced` with the value of 0; see above in `Datatypes & Structures` for details. i.e.
230
239
  `vm_unsynced[$PAGER_NS][page_id] = 0`
231
240
  * the pager's routine of `sync` is called immediately. The page must exist in cache at this point.
232
- * `vm_pg_unmark_needs_sync(ns, page_id)` - Removes the page from the pending synchronization queue `delete vm_unsynced[$PAGER_NS][page_id]`)
241
+ * `vm_pg_unmark_needs_sync(ns, page_id)` - Removes the page from the pending synchronization queue `delete vm_unsynced[$PAGER_NS][page_id]`). If
242
+ it's not in the synchronization queue, then nothing will happend
233
243
 
234
244
  ### <a name='user_page_modification_helpers'></a>User page modification helpers (Controller Macros)
235
245
  You should never directly edit a page in user land; if you do; the pager has no way of knowing that you made modifications. Additionally, if you have multiple controllers watching a page, and it is modified in one controller, those other controllers
@@ -78,7 +78,10 @@ This pager connects to a socket.io server via the `sockio` module.
78
78
  that the endpoint shares the same value as the socket's base pointer.
79
79
  * **Functions**
80
80
  * `init` - Will begin trying to establish a connection to the server. When pages are written,
81
- * `watch` - Signals to the socket.io server that a page is being watched via `watch` event with parameters `{page_id:}`
81
+ * `watch` - Signals to the socket.io server that a page is being watched via `watch` event with parameters `{page_id:}`. Additionally, at a periodic interval (10 seconds) by default,
82
+ the server will be notified of the entire watch-list via the event `resync` which contains the key `watch_list` which will return an array of [page_id, page_hash, page_id, page_hash...] where page_id is the pages `_id` and
83
+ `page_hash` is the page's `__hash`. If the page does not exist, the hash value is null.
84
+
82
85
  * `unwatch` - Signals to the socket.io server that a page is no longer being watched via `unwatch` event with parameters `{page_id:}`
83
86
  * `write` -
84
87
  * Sends the sockio server the `write` event with a hash containing `page`, and optionally (`changes`, and `changes_id`). The `page` contains only the basic
data/docs/todo.md CHANGED
@@ -10,6 +10,8 @@
10
10
  2. The pager synchronization daemon and functions associated with marking paging as needing synchronization will attempt to sync at one time and not
11
11
  store the data if there is a crash or exit before synchronization completes. Furethermore, too many unsynced pages will wreck havok as they wil be
12
12
  dispatched at the same time via the synchronization daemon.
13
+ 3. A `watch` request on a page does technically asynchronously dispatch a disk read request at the same time the pager's request goes through. If a pager was to write
14
+ a page before this disk read, directly via `vm_cache_write`, then the cached page would not be loaded in time.
13
15
 
14
16
  ###Ideas for improvement
15
17
  0. The `Raise` function should not actually signal the controller, it could just inline that
data/lib/flok/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Flok
2
- VERSION = "0.0.82"
2
+ VERSION = "0.0.83"
3
3
  end
data/spec/env/kern.rb CHANGED
@@ -31,8 +31,7 @@ shared_context "kern" do
31
31
 
32
32
  #Will return everything put into the 'dump' dictionary (pre-defined for your convenience)
33
33
  def evald str
34
- self.eval "dump = {}"
35
- self.eval str
34
+ self.eval "dump = {}; #{str}"
36
35
  _dump = self.dump("dump")
37
36
 
38
37
  return DumpHelper.new(_dump)
@@ -204,6 +203,10 @@ shared_context "kern" do
204
203
  end
205
204
  end
206
205
 
206
+ def ignore
207
+ @q.shift
208
+ end
209
+
207
210
  #Expect the queue to not contain a message matching
208
211
  def expect_not_to_contain msg_name, &block
209
212
  original_q = JSON.parse(@q.to_json)
@@ -1,43 +1,38 @@
1
1
  controller :my_controller do
2
2
  services :vm
3
3
 
4
+ on_entry %{
5
+ read_res_params = [];
6
+ }
7
+
4
8
  action :my_action do
5
9
  on_entry %{
6
- var entry = {
7
- hello: "world"
8
- }
9
-
10
- page = NewPage("array", "test");
11
- SetPageHead(page, "head");
12
- SetPageNext(page, "next");
13
- EntryInsert(page, 0, entry);
14
-
15
- page2 = CopyPage(page)
16
- EntryInsert(page, 0, entry);
17
-
18
-
19
- var watch_info = {
20
- ns: "spec",
21
- id: "test"
22
- }
23
-
24
- var write_info = {
25
- ns: "spec",
26
- page: page
27
- };
10
+ }
28
11
 
29
- var write_info2 = {
30
- ns: "spec",
31
- page: page2
32
- };
12
+ on "write_first", %{
13
+ var page = vm_create_page("test")
14
+ info_write = {ns: "spec", page: page}
15
+ Request("vm", "write", info_write);
16
+ context.page = page;
17
+ }
33
18
 
19
+ on "watch_first", %{
20
+ var info_watch = {ns: "spec", id: "test"}
21
+ Request("vm", "watch", info_watch);
22
+ }
34
23
 
35
- read_res_params = [];
36
- Request("vm", "write", write_info);
37
- Request("vm", "watch", watch_info);
38
- Request("vm", "write", write_info2);
24
+ on "modify_first", %{
25
+ var page = vm_copy_page(context.page);
26
+ page.entries.push({
27
+ _sig: "test",
28
+ _id: "test",
29
+ value: "test"
30
+ });
31
+ var info_write = {ns: "spec", page: page};
32
+ Request("vm", "write", info_write);
39
33
  }
40
34
 
35
+
41
36
  on "read_res", %{
42
37
  read_res_params.push(params);
43
38
  }
@@ -0,0 +1,34 @@
1
+ controller :my_controller do
2
+ services :vm
3
+
4
+ action :my_action do
5
+ on_entry %{
6
+ }
7
+
8
+ on "create_page", %{
9
+ var page = vm_create_page("test");
10
+ var info = {ns: "sockio", page: page};
11
+ Request("vm", "write", info);
12
+
13
+ var info2 = {ns: "sockio", id: "test"};
14
+ Request("vm", "watch", info2);
15
+ }
16
+
17
+ on "modify_page", %{
18
+ //Modify the page
19
+ var new_page = vm_copy_page(read_page);
20
+
21
+ new_page.entries.push({
22
+ _id: gen_id(),
23
+ _sig: gen_id(),
24
+ value: "foo"
25
+ });
26
+ var info = {ns: "sockio", page: new_page}
27
+ Request("vm", "write", info);
28
+ }
29
+
30
+ on "read_res", %{
31
+ read_page = params;
32
+ }
33
+ end
34
+ end
@@ -3,16 +3,17 @@ controller :my_controller do
3
3
 
4
4
  action :my_action do
5
5
  on_entry %{
6
- var watch_info = {
7
- ns: "sockio",
8
- id: "test",
9
- };
10
-
6
+ var watch_info = { ns: "sockio", id: "test", };
11
7
  Request("vm", "watch", watch_info);
12
8
  }
13
9
 
14
10
  on "read_res", %{
15
11
  read_res_params = params;
16
12
  }
13
+
14
+ on "unwatch", %{
15
+ var unwatch_info = { ns: "sockio", id: "test"};
16
+ Request("vm", "unwatch", unwatch_info);
17
+ }
17
18
  end
18
19
  end
@@ -0,0 +1,31 @@
1
+ controller :my_controller do
2
+ services :vm
3
+
4
+ action :my_action do
5
+ on_entry %{
6
+ //Setup watch
7
+ var watch_info0 = { ns: "sockio", id: "test0", };
8
+ var watch_info1 = { ns: "sockio", id: "test1", };
9
+
10
+ Request("vm", "watch", watch_info0);
11
+ Request("vm", "watch", watch_info1);
12
+ }
13
+
14
+ on "write_test1", %{
15
+ //Write test1
16
+ var test1 = vm_create_page("test1");
17
+ var write_info1 = { ns: "sockio", page: test1};
18
+ Request("vm", "write", write_info1);
19
+ }
20
+
21
+ on "read_res", %{
22
+ read_res_params = params;
23
+ }
24
+
25
+ #Unwatch test1 page
26
+ on "unwatch_test1", %{
27
+ var unwatch_info1 = { ns: "sockio", id: "test1", };
28
+ Request("vm", "unwatch", unwatch_info1);
29
+ }
30
+ end
31
+ end
@@ -363,32 +363,35 @@ RSpec.describe "kern:vm_service" do
363
363
  expect(read_res_params).to eq(vm_write_list)
364
364
  end
365
365
 
366
- it "non-sync watch does send two watch callbacks to a controller if there is cached content" do
366
+ it "non-sync watch does send two watch callbacks to a controller if there is cached content followed by a write" do
367
367
  ctx = flok_new_user File.read('./spec/kern/assets/vm/controller12.rb'), File.read("./spec/kern/assets/vm/config4.rb")
368
368
 
369
369
  ctx.eval %{
370
370
  base = _embed("my_controller", 1, {}, null);
371
371
  }
372
372
 
373
- #Should not have read anything at this point in time
374
- read_res_params = JSON.parse(ctx.eval("JSON.stringify(read_res_params)"))
375
- expect(read_res_params.length).to eq(0)
373
+ #Step 1. Write a page into cache
374
+ ################################################################################
375
+ #Trigger controller 'write_first'
376
+ @driver.int "int_event", [ ctx.eval("base"), "write_first", {} ]
376
377
 
377
- ctx.eval("int_dispatch([])")
378
+ #Write should have trigger a disk read (to ensure there is no page in cache) vhich we respond
379
+ #with nothing
380
+ @driver.ignore_up_to "if_per_get"
381
+ @driver.int "int_per_get_res", ["vm", "spec", "test", nil]
382
+ ################################################################################
378
383
 
379
- #Now we read the first after it de-queued
380
- read_res_params = JSON.parse(ctx.eval("JSON.stringify(read_res_params)"))
381
- expect(read_res_params.length).to eq(1)
384
+ #Step 2. Watch that page
385
+ ################################################################################
386
+ #Trigger controller 'watch_first'
387
+ @driver.int "int_event", [ ctx.eval("base"), "watch_first", {} ]
382
388
 
383
- ctx.eval("int_dispatch([])")
389
+ #Asynchronous dispatch
390
+ 100.times { @ctx.eval("int_dispatch()")}
384
391
 
385
- #And now the second
386
- read_res_params = JSON.parse(ctx.eval("JSON.stringify(read_res_params)"))
387
- expect(read_res_params.length).to eq(2)
388
-
389
- #And they should have been read in order
390
- vm_write_list = JSON.parse(ctx.eval("JSON.stringify(vm_write_list)"));
391
- expect(read_res_params).to eq(vm_write_list)
392
+ #Should have triggered the read_res
393
+ expect(@ctx.dump("read_res_params").count).to eq(1)
394
+ ################################################################################
392
395
  end
393
396
 
394
397
  it "vm_cache_write does not tell controllers an update has occurred if the page requested to cache was already cached" do
@@ -60,6 +60,26 @@ RSpec.describe "kern:sockio_pager" do
60
60
  @driver.mexpect "if_sockio_fwd", [Integer, "update", dump["pg_sockio0_bp"]], 1
61
61
  end
62
62
 
63
+ it "Does send an unwatch request via socket.io when a page is unwatched" do
64
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
65
+ ctx.eval %{
66
+ //Call embed on main root view
67
+ base = _embed("my_controller", 0, {}, null);
68
+
69
+ //Drain queue
70
+ int_dispatch([]);
71
+ }
72
+
73
+ @driver.int "int_event", [ @ctx.eval("base"), "unwatch", {} ]
74
+
75
+ #Expect an unwatch request
76
+ @driver.ignore_up_to "if_sockio_send", 1 do |e|
77
+ e[1] == "unwatch"
78
+ end
79
+ unwatch_msg = @driver.get "if_sockio_send", 1
80
+ expect(unwatch_msg[2]).to eq({"page_id" => "test"})
81
+ end
82
+
63
83
  it "Does send a watch request via socket.io when a page is watched" do
64
84
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
65
85
  ctx.eval %{
@@ -77,7 +97,90 @@ RSpec.describe "kern:sockio_pager" do
77
97
  }], 1
78
98
  end
79
99
 
80
- it "Does write a page to vm_cache that **does** already exist as <unbased, nochanges> the page receives an 'update' response from the external socket.io without a changes id" do
100
+ it "Does send a watch request at periodic intervals of all pages that are currently watched and then does not send pages that have been unwatched" do
101
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch3.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
102
+ ctx.eval %{
103
+ //Call embed on main root view
104
+ base = _embed("my_controller", 0, {}, null);
105
+
106
+ //Drain queue
107
+ int_dispatch([]);
108
+ }
109
+
110
+ #Get through the first two watches triggered by the original watch
111
+ @driver.ignore_up_to "if_sockio_send", 1
112
+ @driver.mexpect "if_sockio_send", [Integer, "watch", {
113
+ "page_id" => "test0"
114
+ }], 1
115
+ @driver.ignore_up_to "if_sockio_send", 1
116
+ @driver.mexpect "if_sockio_send", [Integer, "watch", {
117
+ "page_id" => "test1"
118
+ }], 1
119
+
120
+ #Now we wait 15 seconds
121
+ (15*4).times { @driver.int "int_timer" }
122
+
123
+ #Now we should have a message for the synhronization of watchlist
124
+ @driver.ignore_up_to("if_sockio_send", 1) { |e| next e[1] == "resync" }
125
+ resync_res = @driver.get "if_sockio_send", 1
126
+ resync_info = resync_res[2] #Hash on end contains the actual data from the message
127
+ expect(resync_info.keys).to include("watch_list"); #Should have a watch list
128
+
129
+ #Check the watchlist we got, first get the hash values for the pages
130
+ expected_watch_list = []
131
+ expected_watch_list += ["test0", nil]
132
+ expected_watch_list += ["test1", nil]
133
+ expect(resync_info["watch_list"]).to eq(expected_watch_list)
134
+
135
+ #Test 2 - Now we are changing a page, so test1 should have a hash value
136
+ #######################################################################################################
137
+ #Update test1, which will try to read from disk, respond with a blank page
138
+ @driver.int "int_event", [ @ctx.eval("base"), "write_test1", {} ]
139
+ @driver.ignore_up_to "if_per_get", 2 do |e|
140
+ next e[2] == "test1"
141
+ end
142
+ @driver.int "int_per_get_res", ["vm", "sockio", "test1", nil]
143
+
144
+ #Now we wait 15 seconds (again)
145
+ (15*4).times { @driver.int "int_timer" }
146
+
147
+ #Now we should have a message for the synhronization of watchlist
148
+ @driver.ignore #it's incomplete... so
149
+ @driver.ignore_up_to("if_sockio_send", 1) { |e| next e[1] == "resync" }
150
+ resync_res = @driver.get "if_sockio_send", 1
151
+ resync_info = resync_res[2] #Hash on end contains the actual data from the message
152
+ expect(resync_info.keys).to include("watch_list"); #Should have a watch list
153
+
154
+ #Check the watchlist we got, first get the hash values for the pages
155
+ expected_watch_list = []
156
+ expected_watch_list += ["test0", nil]
157
+ expected_watch_list += ["test1", @ctx.eval("vm_cache.sockio.test1._hash")]
158
+ expect(resync_info["watch_list"]).to eq(expected_watch_list)
159
+ #######################################################################################################
160
+
161
+ #Test 3 - Now we unwatch a page
162
+ #######################################################################################################
163
+ #Unwatch test1
164
+ @driver.int "int_event", [ @ctx.eval("base"), "unwatch_test1", {} ]
165
+
166
+ #Now we wait 15 seconds (again)
167
+ (15*4).times { @driver.int "int_timer" }
168
+
169
+ #Now we should have a message for the synhronization of watchlist
170
+ @driver.ignore #it's incomplete... so
171
+ @driver.ignore_up_to("if_sockio_send", 1) { |e| next e[1] == "resync" }
172
+ resync_res = @driver.get "if_sockio_send", 1
173
+ resync_info = resync_res[2] #Hash on end contains the actual data from the message
174
+ expect(resync_info.keys).to include("watch_list"); #Should have a watch list
175
+
176
+ #Check the watchlist we got, first get the hash values for the pages
177
+ expected_watch_list = []
178
+ expected_watch_list += ["test0", nil]
179
+ expect(resync_info["watch_list"]).to eq(expected_watch_list)
180
+ #######################################################################################################
181
+ end
182
+
183
+ it "Does write a page to vm_cache that **does** already exist as <unbased, nochanges> the page receives an 'update' response from the external socket.io without a changes id (server result should be written into cache as-is); should no longer exist in unsynced" do
81
184
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
82
185
  dump = ctx.evald %{
83
186
  //Call embed on main root view
@@ -88,6 +191,9 @@ RSpec.describe "kern:sockio_pager" do
88
191
 
89
192
  //pg_sockio0 socket address & the endpoint for the event callback
90
193
  dump.pg_sockio0_bp = pg_sockio0_bp;
194
+
195
+ //Mark page as unsynced manually
196
+ vm_unsynced["sockio"]["test"] = 0;
91
197
  }
92
198
 
93
199
  #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
@@ -144,9 +250,14 @@ RSpec.describe "kern:sockio_pager" do
144
250
  expect(post_read_res_dump["read_res_params"][1]["entries"]).to eq([
145
251
  {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"}
146
252
  ])
253
+
254
+ #Should no longer be unsynced
255
+ vm_unsynced = @ctx.dump("vm_unsynced")
256
+ expect(vm_unsynced["sockio"]).to eq({
257
+ })
147
258
  end
148
259
 
149
- it "Does write a page to vm_cache that **does** already exist as <unbased, changes> receives an 'update' response from the external socket.io without a changes_id" do
260
+ it "Does write a page to vm_cache that **does** already exist as <unbased, changes> receives an 'update' response from the external socket.io without a changes_id. Should still be in the vm_unsynced" do
150
261
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
151
262
  dump = ctx.evald %{
152
263
  //Call embed on main root view
@@ -157,6 +268,9 @@ RSpec.describe "kern:sockio_pager" do
157
268
 
158
269
  //pg_sockio0 socket address & the endpoint for the event callback
159
270
  dump.pg_sockio0_bp = pg_sockio0_bp;
271
+
272
+ //Mark page as unsynced manually
273
+ vm_unsynced["sockio"]["test"] = 0;
160
274
  }
161
275
 
162
276
  #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
@@ -225,9 +339,195 @@ RSpec.describe "kern:sockio_pager" do
225
339
  {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"},
226
340
  {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
227
341
  ])
342
+
343
+ #Should still be unsynced as it contains changes (we only removed changes on __base which is double buffered)
344
+ vm_unsynced = @ctx.dump("vm_unsynced")
345
+ expect(vm_unsynced["sockio"]).to eq({
346
+ "test" => 0
347
+ })
348
+
349
+ end
350
+
351
+ it "Does write a page to vm_cache that **does** already exist as <unbased, changes> receives an 'update' response from the external socket.io with a mis-matching changes_id. Should still be in the vm_unsynced" do
352
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
353
+ dump = ctx.evald %{
354
+ //Call embed on main root view
355
+ dump.base = _embed("my_controller", 0, {}, null);
356
+
357
+ //Drain queue
358
+ int_dispatch([]);
359
+
360
+ //pg_sockio0 socket address & the endpoint for the event callback
361
+ dump.pg_sockio0_bp = pg_sockio0_bp;
362
+
363
+ //Mark page as unsynced manually
364
+ vm_unsynced["sockio"]["test"] = 0;
365
+ }
366
+
367
+ #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
368
+ @driver.ignore_up_to "if_sockio_send"
369
+ @driver.mexpect "if_sockio_send", [Integer, "watch", {
370
+ "page_id" => "test"
371
+ }], 1
372
+
373
+ #The disk should have been signaled
374
+ @driver.ignore_up_to "if_per_get"
375
+ @driver.mexpect "if_per_get", ["vm", "sockio", "test"], 2
376
+
377
+
378
+ #The disk should respond with a page that contains changes
379
+ @driver.int "int_per_get_res", ["vm", "sockio", "test", {
380
+ "_id" => "test",
381
+ "_next" => nil,
382
+ "_head" => nil,
383
+ "entries" => [
384
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
385
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}
386
+ ],
387
+ "__changes" => [
388
+ ["+", 0, {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"}],
389
+ ["+", 1, {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}],
390
+ ["-", "foo3"],
391
+ ],
392
+ "__changes_id" => "foo"
393
+ }]
394
+
395
+ #We (driver sockio) received a watch request for a page with the id 'test'
396
+ #Now we are imagining that the socket.io driver received back some
397
+ #data and is now signaling to the kernel that data is available (as it sends to an
398
+ #event endpoint equal to the socket bp)
399
+ @driver.int "int_event", [dump["pg_sockio0_bp"], "update", {page: {
400
+ _id: "test",
401
+ _next: nil,
402
+ _head: nil,
403
+ entries: [
404
+ {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"},
405
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"}
406
+ ],
407
+ }, changes_id: "foo2"}]
408
+
409
+ post_read_res_dump = ctx.evald %{
410
+ for (var i = 0; i < 100; ++i) {
411
+ //Drain queue (vm_cache_write defers to controller)
412
+ int_dispatch([]);
413
+ }
414
+
415
+ dump.read_res_params = read_res_params;
416
+ }
417
+
418
+ #The controller should have received a notification that a page was updated twice, one
419
+ #for the disk response and one for the pager response
420
+ expect(post_read_res_dump["read_res_params"].length).to eq(2)
421
+ expect(post_read_res_dump["read_res_params"][0]["entries"]).to eq([
422
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
423
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}
424
+ ])
425
+
426
+ #Next page should be rebased ontop of the incomming page, such that changes are played *over* it
427
+ #which includes deletion of foo3
428
+ expect(post_read_res_dump["read_res_params"][1]["entries"]).to eq([
429
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
430
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"},
431
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
432
+ ])
433
+
434
+ #Should still be unsynced as it contains changes (we only removed changes on __base which is double buffered)
435
+ vm_unsynced = @ctx.dump("vm_unsynced")
436
+ expect(vm_unsynced["sockio"]).to eq({
437
+ "test" => 0
438
+ })
439
+ end
440
+
441
+ it "Does write a page to vm_cache that **does** already exist as <unbased, changes> receives an 'update' response from the external socket.io with matching changes_id. Should not still be in the vm_unsynced" do
442
+ ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
443
+ dump = ctx.evald %{
444
+ //Call embed on main root view
445
+ dump.base = _embed("my_controller", 0, {}, null);
446
+
447
+ //Drain queue
448
+ int_dispatch([]);
449
+
450
+ //pg_sockio0 socket address & the endpoint for the event callback
451
+ dump.pg_sockio0_bp = pg_sockio0_bp;
452
+
453
+ //Mark page as unsynced manually
454
+ vm_unsynced["sockio"]["test"] = 0;
455
+ }
456
+
457
+ #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
458
+ @driver.ignore_up_to "if_sockio_send"
459
+ @driver.mexpect "if_sockio_send", [Integer, "watch", {
460
+ "page_id" => "test"
461
+ }], 1
462
+
463
+ #The disk should have been signaled
464
+ @driver.ignore_up_to "if_per_get"
465
+ @driver.mexpect "if_per_get", ["vm", "sockio", "test"], 2
466
+
467
+
468
+ #The disk should respond with a page that contains changes
469
+ @driver.int "int_per_get_res", ["vm", "sockio", "test", {
470
+ "_id" => "test",
471
+ "_next" => nil,
472
+ "_head" => nil,
473
+ "entries" => [
474
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
475
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}
476
+ ],
477
+ "__changes" => [
478
+ ["+", 0, {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"}],
479
+ ["+", 1, {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}],
480
+ ["-", "foo3"],
481
+ ],
482
+ "__changes_id" => "foo"
483
+ }]
484
+
485
+ #We (driver sockio) received a watch request for a page with the id 'test'
486
+ #Now we are imagining that the socket.io driver received back some
487
+ #data and is now signaling to the kernel that data is available (as it sends to an
488
+ #event endpoint equal to the socket bp)
489
+ @driver.int "int_event", [dump["pg_sockio0_bp"], "update", {page: {
490
+ _id: "test",
491
+ _next: nil,
492
+ _head: nil,
493
+ entries: [
494
+ {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"},
495
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"}
496
+ ],
497
+ }, changes_id: "foo"}]
498
+
499
+ post_read_res_dump = ctx.evald %{
500
+ for (var i = 0; i < 100; ++i) {
501
+ //Drain queue (vm_cache_write defers to controller)
502
+ int_dispatch([]);
503
+ }
504
+
505
+ dump.read_res_params = read_res_params;
506
+ }
507
+
508
+ #The controller should have received a notification that a page was updated twice, one
509
+ #for the disk response and one for the pager response
510
+ expect(post_read_res_dump["read_res_params"].length).to eq(2)
511
+ expect(post_read_res_dump["read_res_params"][0]["entries"]).to eq([
512
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
513
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}
514
+ ])
515
+
516
+ #Next page should be rebased ontop of the incomming page, such that changes are played *over* it
517
+ #which includes deletion of foo3
518
+ expect(post_read_res_dump["read_res_params"][1]["entries"]).to eq([
519
+ {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"},
520
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
521
+ ])
522
+
523
+ #Should still be unsynced as it contains changes (we only removed changes on __base which is double buffered)
524
+ vm_unsynced = @ctx.dump("vm_unsynced")
525
+ expect(vm_unsynced["sockio"]).to eq({
526
+ })
228
527
  end
229
528
 
230
- it "Does write a page to vm_cache that **does** already exist as <based<unbased, changes>, changes> receives an 'update' response from the external socket.io without a changes_id" do
529
+
530
+ it "Does write a page to vm_cache that **does** already exist as <based<unbased, changes>, changes> receives an 'update' response from the external socket.io without a changes_id. Should still exist in vm_unsynced" do
231
531
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
232
532
  dump = ctx.evald %{
233
533
  //Call embed on main root view
@@ -238,6 +538,9 @@ RSpec.describe "kern:sockio_pager" do
238
538
 
239
539
  //pg_sockio0 socket address & the endpoint for the event callback
240
540
  dump.pg_sockio0_bp = pg_sockio0_bp;
541
+
542
+ //Mark page as unsynced manually
543
+ vm_unsynced["sockio"]["test"] = 0;
241
544
  }
242
545
 
243
546
  #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
@@ -323,9 +626,15 @@ RSpec.describe "kern:sockio_pager" do
323
626
  {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
324
627
  {"_id" => "foo5", "_sig" => "foo5", "value" => "bar5"},
325
628
  ])
629
+
630
+ #Should still be unsynced as it contains changes (we only removed changes on __base which is double buffered)
631
+ vm_unsynced = @ctx.dump("vm_unsynced")
632
+ expect(vm_unsynced["sockio"]).to eq({
633
+ "test" => 0
634
+ })
326
635
  end
327
636
 
328
- it "Does write a page to vm_cache that **does** already exist as <unbased, changes> receives an 'update' response from the external socket.io **with** an existing changes_id" do
637
+ it "Does write a page to vm_cache that **does** already exist as [changes, based[changes, unbased]] receives an 'update' response from the external socket.io **with** an existing changes_id but keeps that page in vm_unsynced" do
329
638
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch2.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
330
639
  dump = ctx.evald %{
331
640
  //Call embed on main root view
@@ -336,6 +645,9 @@ RSpec.describe "kern:sockio_pager" do
336
645
 
337
646
  //pg_sockio0 socket address & the endpoint for the event callback
338
647
  dump.pg_sockio0_bp = pg_sockio0_bp;
648
+
649
+ //Mark page as unsynced manually
650
+ vm_unsynced["sockio"]["test"] = 0;
339
651
  }
340
652
 
341
653
  #sockio driver should have been signaled (which it should respond asynchronously, and presumabely, after the disk)
@@ -349,7 +661,7 @@ RSpec.describe "kern:sockio_pager" do
349
661
  @driver.mexpect "if_per_get", ["vm", "sockio", "test"], 2
350
662
 
351
663
 
352
- #The disk should respond with a page that contains changes
664
+ #The disk should respond with a page that contains <based<nobase, changes>, changes>
353
665
  @driver.int "int_per_get_res", ["vm", "sockio", "test", {
354
666
  "_id" => "test",
355
667
  "_next" => nil,
@@ -363,25 +675,37 @@ RSpec.describe "kern:sockio_pager" do
363
675
  ["+", 1, {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}],
364
676
  ["-", "foo3"],
365
677
  ],
366
- "__changes_id" => "foo"
678
+ "__changes_id" => "foo",
679
+ "__base" => {
680
+ "_id" => "test",
681
+ "_next" => nil,
682
+ "_head" => nil,
683
+ "entries" => [
684
+ {"_id" => "fooX", "_sig" => "fooX", "value" => "barX"},
685
+ {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"}
686
+ ],
687
+ "__changes_id" => "foo2",
688
+ "__changes" => [
689
+ ["-", "fooX"],
690
+ ["+", 1, {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"}]
691
+ ]
692
+ }
367
693
  }]
368
694
 
369
695
  #We (driver sockio) received a watch request for a page with the id 'test'
370
696
  #Now we are imagining that the socket.io driver received back some
371
697
  #data and is now signaling to the kernel that data is available (as it sends to an
372
698
  #event endpoint equal to the socket bp)
373
- @driver.int "int_event", [dump["pg_sockio0_bp"], "update", {
374
- page: {
375
- _id: "test",
376
- _next: nil,
377
- _head: nil,
378
- entries: [
379
- {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"},
380
- {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"}
381
- ],
382
- },
383
- changes_id: "foo"
384
- }]
699
+ @driver.int "int_event", [dump["pg_sockio0_bp"], "update", {page: {
700
+ _id: "test",
701
+ _next: nil,
702
+ _head: nil,
703
+ entries: [
704
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
705
+ {"_id" => "foo5", "_sig" => "foo5", "value" => "bar5"},
706
+ {"_id" => "fooX", "_sig" => "fooX", "value" => "barX"},
707
+ ],
708
+ }, changes_id: "foo2"}]
385
709
 
386
710
  post_read_res_dump = ctx.evald %{
387
711
  for (var i = 0; i < 100; ++i) {
@@ -399,22 +723,26 @@ RSpec.describe "kern:sockio_pager" do
399
723
  {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
400
724
  {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"}
401
725
  ])
402
- #Changes should be present in second copy
403
- expect(post_read_res_dump["read_res_params"][0]["__changes_id"]).not_to eq(nil)
404
-
405
726
 
406
- #Next page should be rebased ontop of the incomming page, however, since changes_id matches
407
- #current changes on the page, the page is just replaced (as changes were aknowledged and there
408
- #is nothing to repair)
727
+ #Next version is a double replay. First, the server page is called the new 'base', then changes from the
728
+ #old base are played ontop of the server page. Then the top-level changes are recalculated based on this new page,
729
+ #and then replayed on the server's page *again* (a linked copy where the first replayed sits at __base.
409
730
  expect(post_read_res_dump["read_res_params"][1]["entries"]).to eq([
410
- {"_id" => "foo3", "_sig" => "foo3", "value" => "bar3"},
411
- {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
731
+ {"_id" => "foo1", "_sig" => "foo1", "value" => "bar1"},
732
+ {"_id" => "foo2", "_sig" => "foo2", "value" => "bar2"},
733
+ {"_id" => "foo4", "_sig" => "foo4", "value" => "bar4"},
734
+ {"_id" => "foo5", "_sig" => "foo5", "value" => "bar5"},
735
+ {"_id" => "fooX", "_sig" => "fooX", "value" => "barX"},
412
736
  ])
413
- #Changes should not be present in second copy
414
- expect(post_read_res_dump["read_res_params"][1]["__changes_id"]).to eq(nil)
737
+
738
+ #Should still be unsynced as it contains changes (we only removed changes on __base which is double buffered)
739
+ vm_unsynced = @ctx.dump("vm_unsynced")
740
+ expect(vm_unsynced["sockio"]).to eq({
741
+ "test" => 0
742
+ })
415
743
  end
416
744
 
417
- it "Does write a page to vm_cache that **does not** already exist when the page receives an 'update' response from the external socket.io" do
745
+ it "Does write a page to vm_cache that **does not** already exist when the page receives an 'update' response from the external socket.io. Should not exist in vm_unsynced anymore" do
418
746
  ctx = flok_new_user File.read('./spec/kern/assets/vm/pg_sockio/watch.rb'), File.read("./spec/kern/assets/vm/pg_sockio/config.rb")
419
747
  dump = ctx.evald %{
420
748
  //Call embed on main root view
@@ -425,6 +753,9 @@ RSpec.describe "kern:sockio_pager" do
425
753
 
426
754
  //pg_sockio0 socket address & the endpoint for the event callback
427
755
  dump.pg_sockio0_bp = pg_sockio0_bp;
756
+
757
+ //Mark page as unsynced manually
758
+ vm_unsynced["sockio"]["test"] = 0;
428
759
  }
429
760
 
430
761
  #We received a watch request for a page with the id 'test'
@@ -450,6 +781,11 @@ RSpec.describe "kern:sockio_pager" do
450
781
  expect(post_read_res_dump["read_res_params"]["entries"]).to eq([
451
782
  {"_id" => "foo", "_sig" => "foo", "value" => "bar"}
452
783
  ])
784
+
785
+ #Should not be in the queue anymore
786
+ vm_unsynced = @ctx.dump("vm_unsynced")
787
+ expect(vm_unsynced["sockio"]).to eq({
788
+ })
453
789
  end
454
790
 
455
791
  it "Does accept writes of pages that don't currently exist in cache; they go into vm_cache as-is and are sent to the sockio driver" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flok
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.82
4
+ version: 0.0.83
5
5
  platform: ruby
6
6
  authors:
7
7
  - seo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-06 00:00:00.000000000 Z
11
+ date: 2015-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs
@@ -1278,8 +1278,10 @@ files:
1278
1278
  - spec/kern/assets/vm/pg_sockio/config.rb
1279
1279
  - spec/kern/assets/vm/pg_sockio/config_no_url.rb
1280
1280
  - spec/kern/assets/vm/pg_sockio/nothing.rb
1281
+ - spec/kern/assets/vm/pg_sockio/unmark_changes.rb
1281
1282
  - spec/kern/assets/vm/pg_sockio/watch.rb
1282
1283
  - spec/kern/assets/vm/pg_sockio/watch2.rb
1284
+ - spec/kern/assets/vm/pg_sockio/watch3.rb
1283
1285
  - spec/kern/assets/vm/pg_sockio/write.rb
1284
1286
  - spec/kern/assets/vm/pg_sockio/write2.rb
1285
1287
  - spec/kern/assets/vm/pg_sockio/write3.rb
@@ -2230,8 +2232,10 @@ test_files:
2230
2232
  - spec/kern/assets/vm/pg_sockio/config.rb
2231
2233
  - spec/kern/assets/vm/pg_sockio/config_no_url.rb
2232
2234
  - spec/kern/assets/vm/pg_sockio/nothing.rb
2235
+ - spec/kern/assets/vm/pg_sockio/unmark_changes.rb
2233
2236
  - spec/kern/assets/vm/pg_sockio/watch.rb
2234
2237
  - spec/kern/assets/vm/pg_sockio/watch2.rb
2238
+ - spec/kern/assets/vm/pg_sockio/watch3.rb
2235
2239
  - spec/kern/assets/vm/pg_sockio/write.rb
2236
2240
  - spec/kern/assets/vm/pg_sockio/write2.rb
2237
2241
  - spec/kern/assets/vm/pg_sockio/write3.rb