Skip to content

Commit 1e538d3

Browse files
committed
Add read locks to the Lua runner
Support requesting read locks from Lua contracts instead of only write-locks. Signed-off-by: Michael Maurer <[email protected]>
1 parent 376fdf1 commit 1e538d3

File tree

4 files changed

+40
-6
lines changed

4 files changed

+40
-6
lines changed

scripts/gen_bytecode.lua

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ function gen_bytecode()
1010

1111
function get_account(name)
1212
account_key = get_account_key(name)
13-
account_data = coroutine.yield(account_key)
13+
14+
-- 0 is read lock, 1 is write lock
15+
account_data = coroutine.yield(account_key, 1)
1416
if string.len(account_data) > 0 then
1517
account_balance, account_sequence
1618
= string.unpack("I8 I8", account_data)

src/parsec/agent/runners/interface.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace cbdc::parsec::agent::runner {
3838
internal_error,
3939
/// Function yielded more than one key to lock.
4040
yield_count,
41-
/// Function yielded a non-string key.
41+
/// Function yielded a invalid datatype.
4242
yield_type,
4343
/// Error acquiring lock on key.
4444
lock_error,

src/parsec/agent/runners/lua/impl.cpp

+30-4
Original file line numberDiff line numberDiff line change
@@ -138,25 +138,51 @@ namespace cbdc::parsec::agent::runner {
138138
return buf;
139139
}
140140

141+
auto lua_runner::get_stack_integer(int index) -> std::optional<int64_t> {
142+
if(lua_isinteger(m_state.get(), index) != 1) {
143+
return std::nullopt;
144+
}
145+
return lua_tointeger(m_state.get(), index);
146+
}
147+
141148
void lua_runner::schedule_contract() {
142149
int n_results{};
143150
auto resume_ret = lua_resume(m_state.get(), nullptr, 1, &n_results);
144151
if(resume_ret == LUA_YIELD) {
145-
if(n_results != 1) {
146-
m_log->error("Contract yielded more than one key");
152+
if(n_results > 2) {
153+
m_log->error("Contract yielded more than two keys");
147154
m_result_callback(error_code::yield_count);
148155
return;
149156
}
157+
158+
auto lock_level = broker::lock_type::write;
159+
if(n_results == 2) {
160+
auto lock_type = get_stack_integer(-1);
161+
if(!lock_type.has_value()) {
162+
m_log->error("Contract yielded two keys, but the second "
163+
"is not an integer");
164+
m_result_callback(error_code::yield_type);
165+
return;
166+
}
167+
lua_pop(m_state.get(), 1);
168+
169+
lock_level = (lock_type.value() == 0)
170+
? broker::lock_type::read
171+
: broker::lock_type::write;
172+
}
173+
150174
auto key_buf = get_stack_string(-1);
151175
if(!key_buf.has_value()) {
152176
m_log->error("Contract did not yield a string");
153177
m_result_callback(error_code::yield_type);
154178
return;
155179
}
156-
lua_pop(m_state.get(), n_results);
180+
181+
lua_pop(m_state.get(), 1);
182+
157183
auto success
158184
= m_try_lock_callback(std::move(key_buf.value()),
159-
broker::lock_type::write,
185+
lock_level,
160186
[&](auto res) {
161187
handle_try_lock(std::move(res));
162188
});

src/parsec/agent/runners/lua/impl.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ namespace cbdc::parsec::agent::runner {
1818
/// function execution, signature checking and commiting execution results.
1919
/// Class cannot be re-used for different functions/transactions, manages
2020
/// the lifecycle of a single transaction.
21+
/// NOTE: When writing contracts, to pass data between the Lua environment
22+
/// and the C++ environment, use `coroutine.yield()`. To request a
23+
/// read-lock use coroutine.yield(<data>, 0). To request a write-lock use
24+
/// coroutine.yield(<data>, 1) or coroutine.yield(<data>).
2125
class lua_runner : public interface {
2226
public:
2327
/// \copydoc interface::interface()
@@ -47,6 +51,8 @@ namespace cbdc::parsec::agent::runner {
4751

4852
auto get_stack_string(int index) -> std::optional<buffer>;
4953

54+
auto get_stack_integer(int index) -> std::optional<int64_t>;
55+
5056
void schedule_contract();
5157

5258
void

0 commit comments

Comments
 (0)