aboutsummaryrefslogtreecommitdiff
path: root/src/script/cpp_api/s_async.h
blob: 8d612d58cc8fb4fd941fd5fbd584fad97316d171 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
Minetest
Copyright (C) 2013 sapier, <sapier AT gmx DOT net>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef CPP_API_ASYNC_EVENTS_HEADER
#define CPP_API_ASYNC_EVENTS_HEADER

#include <vector>
#include <deque>
#include <map>

#include "threading/thread.h"
#include "threading/mutex.h"
#include "threading/semaphore.h"
#include "debug.h"
#include "lua.h"
#include "cpp_api/s_base.h"

// Forward declarations
class AsyncEngine;


// Declarations

// Data required to queue a job
struct LuaJobInfo {
	// Function to be called in async environment
	std::string serializedFunction;
	// Parameter to be passed to function
	std::string serializedParams;
	// Result of function call
	std::string serializedResult;
	// JobID used to identify a job and match it to callback
	unsigned int id;

	bool valid;
};

// Asynchronous working environment
class AsyncWorkerThread : public Thread, public ScriptApiBase {
public:
	AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name);
	virtual ~AsyncWorkerThread();

	void *run();

private:
	AsyncEngine *jobDispatcher;
};

// Asynchornous thread and job management
class AsyncEngine {
	friend class AsyncWorkerThread;
public:
	AsyncEngine();
	~AsyncEngine();

	/**
	 * Register function to be used within engine
	 * @param name Function name to be used within Lua environment
	 * @param func C function to be called
	 */
	bool registerFunction(const char* name, lua_CFunction func);

	/**
	 * Create async engine tasks and lock function registration
	 * @param numEngines Number of async threads to be started
	 */
	void initialize(unsigned int numEngines);

	/**
	 * Queue an async job
	 * @param func Serialized lua function
	 * @param params Serialized parameters
	 * @return jobid The job is queued
	 */
	unsigned int queueAsyncJob(std::string func, std::string params);

	/**
	 * Engine step to process finished jobs
	 *   the engine step is one way to pass events back, PushFinishedJobs another
	 * @param L The Lua stack
	 */
	void step(lua_State *L);

	/**
	 * Push a list of finished jobs onto the stack
	 * @param L The Lua stack
	 */
	void pushFinishedJobs(lua_State *L);

protected:
	/**
	 * Get a Job from queue to be processed
	 *  this function blocks until a job is ready
	 * @return a job to be processed
	 */
	LuaJobInfo getJob();

	/**
	 * Put a Job result back to result queue
	 * @param result result of completed job
	 */
	void putJobResult(LuaJobInfo result);

	/**
	 * Initialize environment with current registred functions
	 *  this function adds all functions registred by registerFunction to the
	 *  passed lua stack
	 * @param L Lua stack to initialize
	 * @param top Stack position
	 */
	void prepareEnvironment(lua_State* L, int top);

private:
	// Variable locking the engine against further modification
	bool initDone;

	// Internal store for registred functions
	std::map<std::string, lua_CFunction> functionList;

	// Internal counter to create job IDs
	unsigned int jobIdCounter;

	// Mutex to protect job queue
	Mutex jobQueueMutex;

	// Job queue
	std::deque<LuaJobInfo> jobQueue;

	// Mutex to protect result queue
	Mutex resultQueueMutex;
	// Result queue
	std::deque<LuaJobInfo> resultQueue;

	// List of current worker threads
	std::vector<AsyncWorkerThread*> workerThreads;

	// Counter semaphore for job dispatching
	Semaphore jobQueueCounter;
};

#endif // CPP_API_ASYNC_EVENTS_HEADER
">,": This controller had an arrowconn of", atc.controllers[pts].arrowconn, "set. Since this field is now deprecated, it was removed.") atc.controllers[pts].arrowconn = nil end local train = advtrains.trains[train_id] local index = advtrains.path_lookup(train, pos) local iconnid = 1 if index then iconnid = train.path_cn[index] else atwarn("ATC rail at", pos, ": Rail not on train's path! Can't determine arrow direction. Assuming +!") end local command = atc.controllers[pts].command command = eval_conditional(command, iconnid==1, train.velocity) if not command then command="" end command=string.match(command, "^%s*(.*)$") if command == "" then atprint("Sending ATC Command to", train_id, ": Not modifying, conditional evaluated empty.") return true end atc.train_set_command(train, command, iconnid==1) atprint("Sending ATC Command to", train_id, ":", command, "iconnid=",iconnid) return true else atwarn("ATC rail at", pos, ": Sending command failed: The train",train_id,"does not exist. This seems to be a bug.") end else atwarn("ATC rail at", pos, ": Sending command failed: There's no train at this position. This seems to be a bug.") end else atwarn("ATC rail at", pos, ": Sending command failed: Entry for controller not found.") atwarn("ATC rail at", pos, ": Please visit controller and click 'Save'") end return false end -- Resets any ATC commands the train is currently executing, including the target speed (tarvelocity) it is instructed to hold -- if keep_tarvel is set, does not clear the tarvelocity function atc.train_reset_command(train, keep_tarvel) train.atc_command=nil train.atc_delay=nil train.atc_brake_target=nil train.atc_wait_finish=nil train.atc_arrow=nil if not keep_tarvel then train.tarvelocity=nil end end --nodes local idxtrans={static=1, mesecon=2, digiline=3} local apn_func=function(pos) -- FIX for long-persisting ndb bug: there's no node in parameter 2 of this function! local meta=minetest.get_meta(pos) if meta then meta:set_string("infotext", attrans("ATC controller, unconfigured.")) meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) end end advtrains.atc_function = function(def, preset, suffix, rotation) return { after_place_node=apn_func, after_dig_node=function(pos) return advtrains.pcall(function() advtrains.invalidate_all_paths(pos) advtrains.ndb.clear(pos) local pts=minetest.pos_to_string(pos) atc.controllers[pts]=nil end) end, on_receive_fields = function(pos, formname, fields, player) return advtrains.pcall(function() if advtrains.is_protected(pos, player:get_player_name()) then minetest.record_protection_violation(pos, player:get_player_name()) return end local meta=minetest.get_meta(pos) if meta then if not fields.save then --maybe only the dropdown changed if fields.mode then meta:set_string("mode", idxtrans[fields.mode]) if fields.mode=="digiline" then meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) else meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) end meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) end return end meta:set_string("mode", idxtrans[fields.mode]) meta:set_string("command", fields.command) meta:set_string("command_on", fields.command_on) meta:set_string("channel", fields.channel) if fields.mode=="digiline" then meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) ) else meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) ) end meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta)) local pts=minetest.pos_to_string(pos) local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) atc.controllers[pts]={command=fields.command} if #advtrains.occ.get_trains_at(pos) > 0 then atc.send_command(pos) end end end) end, advtrains = { on_train_enter = function(pos, train_id) atc.send_command(pos) end, }, } end function atc.get_atc_controller_formspec(pos, meta) local mode=tonumber(meta:get_string("mode")) or 1 local command=meta:get_string("command") local command_on=meta:get_string("command_on") local channel=meta:get_string("channel") local formspec="size[8,6]" -- "dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]" if mode<3 then formspec=formspec.."field[0.5,1.5;7,1;command;"..attrans("Command")..";"..minetest.formspec_escape(command).."]" if tonumber(mode)==2 then formspec=formspec.."field[0.5,3;7,1;command_on;"..attrans("Command (on)")..";"..minetest.formspec_escape(command_on).."]" end else formspec=formspec.."field[0.5,1.5;7,1;channel;"..attrans("Digiline channel")..";"..minetest.formspec_escape(channel).."]" end return formspec.."button_exit[0.5,4.5;7,1;save;"..attrans("Save").."]" end --from trainlogic.lua train step local matchptn={ ["SM"]=function(id, train) train.tarvelocity=train.max_speed return 2 end, ["S([0-9]+)"]=function(id, train, match) train.tarvelocity=tonumber(match) return #match+1 end, ["B([0-9]+)"]=function(id, train, match) if train.velocity>tonumber(match) then train.atc_brake_target=tonumber(match) if not train.tarvelocity or train.tarvelocity>train.atc_brake_target then train.tarvelocity=train.atc_brake_target end end return #match+1 end, ["BB"]=function(id, train) train.atc_brake_target = -1 train.tarvelocity = 0 return 2 end, ["W"]=function(id, train) train.atc_wait_finish=true return 1 end, ["D([0-9]+)"]=function(id, train, match) train.atc_delay=tonumber(match) return #match+1 end, ["R"]=function(id, train) if train.velocity<=0 then advtrains.invert_train(id) advtrains.train_ensure_init(id, train) -- no one minds if this failed... this shouldn't even be called without train being initialized... else atwarn(sid(id), attrans("ATC Reverse command warning: didn't reverse train, train moving!")) end return 1 end, ["O([LRC])"]=function(id, train, match) local tt={L=-1, R=1, C=0} local arr=train.atc_arrow and 1 or -1 train.door_open = tt[match]*arr return 2 end, } eval_conditional = function(command, arrow, speed) --conditional statement? local is_cond, cond_applies, compare local cond, rest=string.match(command, "^I([%+%-])(.+)$") if cond then is_cond=true if cond=="+" then cond_applies=arrow end if cond=="-" then cond_applies=not arrow end else cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$") if cond and compare then is_cond=true if cond=="<" then cond_applies=speed<tonumber(compare) end