lua simple mutex for threaded app - multithreading

I'm writing extension/plugin for some app.
In its documentation it's said, that there are 2 threads for plugin:
- "main thread" where all business logic must live
- "callback thread" where application invokes callbacks with predefined names by events, these callbacks must not do complicated things and return as soon as possible
documentation is not very clear so it's possible that callbacks are invoked not from one thread but from many threads.
I've wrote dummy mutex, that works like that:
Mutex = class {
_lock = function(self, owner)
assert(owner, 'owner must be set')
local ts = Timestamp.get()
local key = tostring(owner) .. ' ' .. tostring(ts)
self[key] = ts
for k, v in pairs(self) do
if k ~= key and v <= ts then
self[key] = nil
return false
end
end
return key
end,
lock = function(self, owner, wait)
local wait = wait or 0.01
local k
repeat
k = self:_lock(owner)
if k then return k else sleep(wait) end
until k
end,
unlock = function(self, key)
self[key] = nil
end
}
and use it for making thread safe queue like this:
ThreadSafeQueue = class {
new = function(cls)
return getmetatable(cls).new(cls, {
mx_queue = Mutex:new(),
mx_push = Mutex:new(),
})
end,
pop = function(self)
local lock_queue = self.mx_queue:lock(self)
local val
if #self then
val = table.remove(self, 1)
else
val = nil
end
self.mx_queue:unlock(lock_queue)
return val
end,
push = function(self, val)
if val == nil then return end
-- don't `push()` from few threads at the same time
local lock_push = self.mx_push:lock(val)
-- don't `pop()` when `push()` and `push()` when `pop()`
local lock_queue = self.mx_queue:lock(self)
self[#self + 1] = val
self.mx_queue:unlock(lock_queue)
self.mx_push:unlock(lock_push)
end
}
class here is helper that returns object with prototype lookups and :new() method which sets metatable.
Main problem is that I'm not sure what pairs() does.
- If original table will be modified while it's being iterated, will this loop return at least old state?
- Is it possible that some k, v will not be iterated in such case?
Other problem is that application I'm writing for is really black box and I'm not even sure on which os it will run (Win, Mac, Linux).
Everything that I know 100% that I have threads and socket module.
Could you review provided code?
Will it work?
Is there any other possibilities for mutex.
May be socket will give something?

option for socket:
try to create socket, if success, then mutex locked, else - wait it for close
local Mutex = class {
identities = {},
new = function(cls, identity)
assert(not cls.identities[identity])
local inst = getmetatable(cls).new(cls, {
port = identity,
server = nil
})
cls.identities[identity] = inst
return inst
end,
lock = function(self, wait)
local wait = wait or 0.01
local server
local ts = Timestamp.get()
repeat
server = socket.bind("*", self.port)
if server then
self.server = server
return true
else
sleep(wait)
end
assert(Timestamp.get() - ts < 3, 'deadlock')
until server
end,
unlock = function(self)
self.server:close()
self.server = nil
end
}

Related

How would I add other types of actions(status effects, heal, stat boosts, party switching) to a turn based system

I am currently trying to implement some different actions instead of attack, but I want to focus on a party function, that looks like this, I can figure out the types of actions I want but I want to change the battle function to fit into a role that can accept multiple actions and not just attack
func change_party(inventory,index): # Inventory being if enemy or player
if inventory[index].fainted == false: # Checks if the monster is not dead
var hold = inventory[0] # Holds the first slot and then applys it later to the selected slot
inventory[0] = inventory[index]
inventory[index] = hold
This changes the index that was inputed by buttons and swaps it around with the first slot in the array this is because the first slot is what monster is shown first, I also have this battle function:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for attacker in participants:
var player_move = player_inventory[0].move_slot[action_index]
var random_move = randi() % 3 + 1
var enemy_move = attacker.move_slot[0] # Use random_move currently 0
var target = _choose_target(attacker, participants)
if attacker == player_inventory[0]:
yield(attack(attacker, target, player_move), "completed")
else:
yield(attack(attacker, target, enemy_move), "completed")
if player_inventory[0].current_hp <= 0:
player_inventory[0].current_hp = 0
player_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.LOST
return
if enemy_inventory[0].current_hp <= 0:
enemy_inventory[0].current_hp = 0
enemy_inventory[0].fainted = true
Battle_Ui_Updater()
self.current_state = GameState.WON
return
self.current_state = GameState.ACTION
One solution that came to me was trying to have all the actions in a array and just call the action I want based on input but I have no clue how I would make that look readable or bug-free
In this function is how it decides who´s turn it is based on speed, but sometimes I want the player to go first for example when I want to change party members and when the player has changed I want the enemy to start attacking, But I am scratching my head on how would I make it change actions, I know the attack function should be changed if I want it to do something else but I also want to be able to control who´s turn based on what type of action is used, I am sorry if I didnt explain it well, hope you guys understand, I don't want to repeat my self by making another similar to battle function so how do I avoid being repetitive while also doing what I want?
BattleScript:
func attack(attacker, target, move):
print(str(attacker.name) + " used " + str(move.name) + " " + str((move)))
var new_text = (attacker.name + " attacked with " + move.name)
text_scroller(new_text)
var data = recieve_damage(move,attacker,target)
#var data = target.take_damage(move,attacker) # Calls the function to take damage
yield(get_tree().create_timer(2), "timeout") #Wait for 2 seconds
Battle_Ui_Updater()
if data:
yield(critical_hit(attacker),"completed")
func critical_hit(attacker):
var new_text = (attacker.name + " has landed a critical hit! ")
text_scroller(new_text)
print(attacker.name + " has landed a critical hit!")
yield(get_tree().create_timer(2.5), "timeout")
func Get_effectiveness(attack_type,defence_type):
if attack_type == TypeData.types.none or defence_type == TypeData.types.none:
return 1
print("row : " + str(attack_type))
print("col : " + str(defence_type))
return TypeData.chart[attack_type][defence_type]
func recieve_damage(action,attacker,defender):
var critical = 1
var critical_chance = randi() % 100 + 1
if critical_chance <= 6.25:
critical = 2
var attack_mode
var defence_mode
if action.is_special == true:
attack_mode = attacker.special_attack
defence_mode = defender.special_defence
else:
attack_mode = attacker.attack
defence_mode = defender.defence
var type : float = Get_effectiveness(action.type,defender.type_1) * Get_effectiveness(action.type,defender.type_2)
var modifier : float = rand_range(0.85,1.0) * type * critical
var a : float = (2.0 * attacker.level / 5.0 + 2.0)
var b : float = (a * attack_mode * action.power / defence_mode) / 50.0
var c : float = (b + 2.0) * modifier
var damage = int(c)
defender.current_hp -= damage
print(str(attacker.name) + " = " + str(damage))
return critical > 1
Swap Party member with the current one:
func change_party(inventory,index):
if inventory[index].fainted == false:
var hold = inventory[0]
inventory[0] = inventory[index]
inventory[index] = hold
print(inventory[0].hp)
Battle_Ui_Updater()
move_ui_updater(player_inventory[0])
yield(get_tree().create_timer(2),"timeout")
I came up with this:
class_name Task
class Skipper:
signal skip
func emit() -> void:
emit_signal("skip")
var _instance:Object
var _method:String
var _parameters:Array
var _result = null
func _init(instance:Object, method:String, parameters:Array = []) -> void:
_instance = instance
_method = method
_parameters = parameters
func execute():
var instance = _instance
var parameters = _parameters
if instance != null and instance.has_method(_method):
_instance = null
_parameters = []
_result = instance.callv(_method, parameters)
if _result is GDScriptFunctionState && _result.is_valid():
_result = yield(_result, "completed")
return _result
var skipper = Skipper.new()
skipper.call_deferred("emit")
yield(skipper, "skip")
return _result
And this is how you initialize it:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = Task.new(self, "attack", parameters)
And this is how you use it:
yield(player_action.execute(), "completed")
The new thing about this class is that it will be asynchronous regardless of whether the method it calls is asynchronous or not (so you don't have to worry if what it calls yields or not). And it will complete after the method it calls completes, either way. And it even returns (null if what it calls does not return)!
Note: This code will memoize the result, and get rid of the parameters and instance it was linked to. Subsequent calls to execute will simply return the memoized result. This way it does not hold references unecesarily.
How do we do that?
Well, first of all, we are using callv to call a method by name with an array of parameters. There is a class FuncRef in Godot that can be used in similar fashion. However, not using it resulted more convinient.
Second, calling something asynchronous would be yield(..., "completed") but we don't know if what we are calling is asynchronous. So we check. How? If it is asynchronous, it actually returns the state it left the execution as a GDScriptFunctionState, so we check that.
If it is GDScriptFunctionState, then we can use yield(..., "completed") on it.
If it isn't. We need to somehow make the function asynchronous. We are going to do that by emitting a signal with call_deferred. I decided in making it an inner class so that it is not exposed outside of the script.
Also note that the code checks if the instance is not null and has the method passed. But if it does not, it will simply return the result it had stored. This is part of the memoization mechanism, however, it also means that you have no feedback if you passed the wrong instance of wrote the method name wrong.
Finally, it should work with this version of _battle:
func _battle():
participants = [player_inventory[0],enemy_inventory[0]]
participants.sort_custom(self, "check_speed")
for current_participant in participants:
if current_participant == player_inventory[0]:
yield(player_action.execute(), "completed")
else:
yield(enemy_action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION
func decide_battle_state():
if check_fainted(player_inventory[0]):
return GameState.LOST
if check_fainted(enemy_inventory[0]):
return GameState.WON
return GameState.BATTLE
func check_fainted(participant) -> bool:
if participant.current_hp > 0:
return false
participant.current_hp = 0
participant.fainted = true
Battle_Ui_Updater()
return true
You could make an Task with speed:
class_name BattleAction extends Task
# warning-ignore:unused_class_variable
var speed
func _init(instance:Object, method:String, parameters:Array = []).(instance, method, parameters) -> void:
pass
Then use like this:
var parameters = [player_inventory[0], enemy_inventory[0], player_inventory[0].move_slot[action_index]]
player_action = BattleAction.new(self, "attack", parameters)
player_action.speed = player_inventory[0].speed
And finally _battle can look like this:
With your BattleAction which has speed, you can do this:
func _battle():
actions = [player_action, enemy_action]
actions.sort_custom(self, "check_speed")
for action in actions:
yield(action.execute(), "completed")
var state = decide_battle_state()
if state != GameState.BATTLE:
self.current_state = state
return
self.current_state = GameState.ACTION

How to prevent setmetatable to be overridden

I want to share content in a secure way without exposing it to malicious code. Let's say I have a
Base addon
local BaseAddon = {}
local secretTable = {someContent = "hidden content"}
local function index(t,k)
if (k == "SECRET") then
return secretTable
end
end
setmetatable(BaseAddon, {
__index = index,
__newindex = function() end,
__metatable = false, -- disallow setmetatable again
})
return BaseAddon -- or a global hook method...
and a Sub addon that accesses the Base addon hidden content
local SubAddon = require("BaseAddon") -- or a global hook method...
SubAddon = SubAddon["SECRET"]
print(SubAddon.somelib) -- returns content
SubAddon = SubAddon["SECRETS"]
print(SubAddon.somelib) -- returns index local 'SubAddon' (a nil value)
but this is still not safe. I could now just simply do the following to catch my secret:
function newSetmetatable(table, mt)
mt.__metatable = nil
mt.__index = function(t,k)
print(k)
return t[k]
end
return originalSetmetatable (table, mt)
end
originalSetmetatable = setmetatable
setmetatable = newSetmetatable
Is there any way to prevent this or another solution to actually share the secret table?
Your original code is as good as you can get in Lua. Any sort of security like that you set up in Lua needs to either be written in C, or be ran before any untrusted code runs.

Thread freezes on initialising an object in scala referencing the object being initialised

I struggled with a specific problem with java Threads and initialisation of an object in scala. In my original code a Thread freezes. I finally found a solution for my problem. But I do not understand why this works.
I have rewritten the problem into the following code. Running this, the code stops at the first line in the Thread that references to a variable of the object being initialised. If the reference is through the self variable which points to this initialising object, it executes fine.
object Alpha {
var r = "A"
r = "B"
System.out.println("Not threaded: r = " + r)
val thread =
{
val self = this
new Thread(
new Runnable
{
def run() =
{
System.out.println(" Started.")
System.out.println(" Threaded self.r = " + self.r)
self.r = "C"
System.out.println(" Threaded self.r = " + self.r)
// At the following line the thread freezes!
System.out.println(" Threaded r = " + r)
r = "D"
System.out.println(" Threaded r = " + r)
}
})
}
thread.start
thread.join
}
Calling Alpha will result in the following output before execution is frozen.
Not threaded: r = B
Started.
Threaded self.r = B
Threaded self.r = C
I would understand if referencing to object variables during initialisation is prohibited at all times. But now it looks a bit randomly.

when a spawn object moves across the screen, I want it to despawn once it gets to a certain x value?

I have random spawned objects that automatically moves across the screen. I want it so that when the objects reach a certain x position it despawns itself.
local mRandom = math.random
local objects = {"Vehicle11" ,"Vehicle21","Vehicle31","Vehicle41"}
local objectTag = 0
local object = {}
local function spawncarright()
local rightcar = {408,312}
objectTag = objectTag + 1
local objIdx = mRandom(#objects)
local objName = objects[objIdx]
object[objectTag] = display.newImage(objName..".png") -- see the difference here
object[objectTag].x = 32
object[objectTag].y = rightcar[math.random(1,2)]
object[objectTag].name = objectTag
transition.to(object[objectTag], {time = 3500, x = 348})
end
timer.performWithDelay(2000,spawncarright,0)
so once reaches object[objectTag].x = 348 the object despawn
Try this:
local function deSpawn()
for i=1,objectTag do
if(object[i]~=nil and object[i].x~=nil and object[i].x>=348)then
-- If you want to remove the object, then use the following 2 lines --
object[i]:removeSelf()
print("Removed object["..i.."]")
--or else if you want to reposition the object, then uncomment the following --
--[[
spawncarright()
--]]
end
end
end
Runtime:addEventListener("enterFrame",deSpawn)
Keep coding................... :)
You should do it within the transition.to call:
object[objectTag].deleteSelf = function(self)
object[self.name] = nil -- Remove reference to object in table
display.remove(self)
self = nil
end
local localObj = object[objectTag] -- Do this so the object doesn't change with the objectTag does; if objectTag is incremented, then when the transition ends, it won't be pointing to the same object when we call the function
transition.to(localObj, {time = 3500, x = 348, onComplete = function() localObj:deleteSelf() end})

How to get the result of a callback function before continuing the main function?

I use a recursive function to construct a JSON data containing (URL, children, data). I send this data to my graph (RGraph library) in order to create it. The problem is that in my function creer_json = (url, nom, recursif, recursif_max) I have a problem. Here's the code :
creer_json = (url, nom, recursif, recursif_max) ->
recursif--
resultat = {}
#tab = []
tableau = getBody(url,(error,message) ->
#tab = getTab(message.body))
tab_children = []
tab_relation = []
indice = 0
id_enfant = 1
adresse = "<h1>Liens de "+url+"</h1>"
while indice < tab.length
if (recursif == recursif_max-1)
id_urlfils = id_enfant
else
id_urlfils = nom+"."+id_enfant
adresse = adresse+" "+"<li>"+id_urlfils+" : "+""+tab[indice]+"</li>"
indice++
id_enfant++
tab_relation.push("<ul>"+adresse+"</ul>")
id_url = 1
i = 0
while i < tab.length
if (recursif == recursif_max-1)
id_urlfils = id_url
else
id_urlfils = nom+"."+id_url
if recursif >= 0
json2 = creer_json(tab[i], id_urlfils, recursif, recursif_max)
tab_children.push(json2)
i++
id_url++
resultat =
id : nom
name : nom
children : tab_children
data : { relation: tab_relation }
return resultat
My problem is that i need the result of the fourth instruction to continue the main function :
tableau = getBody(url,(error,message) ->
#tab = getTab(message.body))
#tab contains all the URL of a website and i have to loop on them to construct the JSON data.
The main function continue without the result of #tab and I NEED that data! My problem may not be clear so don't hesitate to ask me if you don't understand. Thank you in advance for your time.
As Eru wrote: If you need the result of an asynchronous call, you have to continue in the callback. You can’t re-synchronize an async call. This also means that you can’t return anything useful from creer_json. Instead, if you need the return value, you have to add a callback parameter which gets the return value passed. What’s more, since creer_json would become an asynchronous function and you call it recursively, these recursive calls would need to be with callbacks.

Resources