commit - d1d1722e0b50fe00258caf09f715a2703160fbdc
commit + 897a4d92041aabef45cfe2e5d1de692d931d8ed0
blob - 39595320a8ac12823d56ec1c3977b98aa4fb2acb
blob + 74708d3ef4936fc54882ae5b3a8ad12332da9429
--- CHANGELOG.md
+++ CHANGELOG.md
### Added
+- A CAS-register generator.
+
### Changed
- Bump luacheck version.
blob - 9a835425d5023728e3a352f7e9ad0fdf7b5d438d
blob + 80ebdc35abdf22cf218ddbf61d1f567213f9002f
--- molly/tests.lua
+++ molly/tests.lua
-- except nil. Null values in Lua tables are represented as JSON null
-- (`json.NULL`, a Lua `lightuserdata` NULL pointer) is provided for
-- comparison.
+--
+--### CAS-Register
+--
+-- Generator produces concurrent atomic updates to a shared
+-- register. Writes are assumed to be unique, but this is the only
+-- constraint.
+--
+-- Operations are of three forms:
+--
+-- { "r", "x", 1 } denotes a read of `x` observing the value 1.
+-- { "w", "x", 2 } denotes a write of `x`, settings its value to 2.
+-- { "cas", "x", 2 } denotes a CAS of `x`, settings its value to 2.
+--
+-- Example of history:
+--
+-- { type = "invoke", f = "cas", value = { 1, 5 }, process = 0, index = 1}
+-- { type = "ok", f = "fail", value = { 1, 5 }, process = 0, index = 2}
+-- { type = "invoke", f = "write", value = { 2 }, process = 0, index = 3}
+-- { type = "ok", f = "write", value = { 2 }, process = 0, index = 4}
+--
local math = require('math')
-- @function rw_register_gen
local function rw_register_gen()
return gen_lib.cycle(gen_lib.iter({ op_r, op_w }))
+end
+
+-- Function that describes a 'read' operation.
+local function cas_op_r()
+ return setmetatable({
+ f = 'read',
+ value = {
+ json.NULL,
+ },
+ }, {
+ __type = '<operation>',
+ __tostring = function(self)
+ return '<read>'
+ end,
+ })
+end
+
+-- Function that describes a 'write' operation.
+local function cas_op_w()
+ return setmetatable({
+ f = 'write',
+ value = {
+ math.random(1, 100),
+ }
+ }, {
+ __type = '<operation>',
+ __tostring = function(self)
+ return '<write>'
+ end,
+ })
end
+-- Function that describes a 'cas' operation.
+local function cas_op_cas()
+ return setmetatable({
+ f = 'cas',
+ value = {
+ math.random(1, 100),
+ math.random(1, 100),
+ }
+ }, {
+ __type = '<operation>',
+ __tostring = function(self)
+ return '<cas>'
+ end,
+ })
+end
+
+--- CAS (Compare-And-Set) operations generator.
+--
+-- @usage
+--
+-- > log = require('log')
+-- > tests = require('molly.tests')
+-- > for _it, v in tests.cas_register_gen() do log.info(v()) end
+-- {"f":"read","value":[null]}
+-- {"f":"write","value":[80]}
+-- {"f":"cas","value":[70,60]}
+-- {"f":"read","value":[null]}
+-- {"f":"write","value":[76]}
+-- {"f":"cas","value":[9,67]}
+-- {"f":"read","value":[null]}
+-- {"f":"write","value":[34]}
+-- {"f":"cas","value":[74,43]}
+-- {"f":"read","value":[null]}
+-- ---
+-- ...
+--
+-- @return an iterator
+--
+-- @function cas_register_gen
+local function cas_register_gen()
+ return gen_lib.cycle(gen_lib.iter({ cas_op_r, cas_op_w, cas_op_cas }))
+end
+
-- Function that describes a 'list' micro operation.
local function mop_list(key_count)
return {
return {
list_append_gen = list_append_gen,
rw_register_gen = rw_register_gen,
+ cas_register_gen = cas_register_gen,
}
blob - 5c6f8131b0a73944141e4af7301b5dcb258f2599
blob + 176082a51d2c02c36c3f6ff9a2b36654b8135d18
--- test/tests.lua
+++ test/tests.lua
local seed = os.time()
math.randomseed(seed)
-test:plan(10)
+test:plan(11)
test:test('clock', function(test)
test:plan(5)
test:ok(passed_time - timeout < eps, "gen.time_limit()")
end)
+test:test('tests.cas_register_gen', function(test)
+ test:plan(4)
+
+ local num = 5
+ local n = tests.cas_register_gen():take(5):length()
+ test:is(n, num, 'tests.cas_register_gen(): length')
+
+ local gen, param, state = tests.cas_register_gen()
+ local _, res = gen(param, state)
+ res = res()
+ local f = res.f
+ local value = res.value
+ test:ok(f == 'cas' or
+ f == 'write' or
+ f == 'read', 'tests.cas_register_gen(): function')
+ test:is(type(value), 'table', 'tests.cas_register_gen(): value type')
+ if f == 'cas' then
+ test:ok(#value, 2, 'tests.cas_register_gen(): cas value')
+ end
+ if f == 'read' then
+ test:ok(#value, 0, 'tests.cas_register_gen(): read value')
+ end
+ if f == 'write' then
+ test:ok(#value, 1, 'tests.cas_register_gen(): write value')
+ end
+end)
+
local IDX_MOP_TYPE = 1
local IDX_MOP_KEY = 2
local IDX_MOP_VAL = 3