Here's some simple code that demonstrates what I'm trying to do
myVar = 1
reader = () ->
getDataFromServer1().then ->
# uses myVar and does stuff according to its value
# returns promise
writer = () ->
getDataFromServer2().then ->
# assigns to myVar
# returns promise
Q.all([reader(), reader(), reader(), writer(), writer(), writer()]).done ->
console.log 'done'
So I have multiple threads running at the same time. some of them change the value of myVar
and some read the value and rely on it. And I don't want a writer to write while another writer is writing or a reader is reading. Readers can read at the same time though. This is similar to the Readers–writers problem.
I tried to solve this by defining a sharedResource
function as follows
sharedResource = (initialValue) ->
readLock = Q.fcall ->
writeLock = Q.fcall ->
value: initialValue
read: (action) ->
newPromise = writeLock.then action
readLock = Q.all([newPromise, readLock]).then -> null
newPromise
write: (action) ->
newPromise = Q.all([readLock, writeLock]).then action
writeLock = Q.all([newPromise, writeLock]).then -> null
newPromise
and then changed my code to use it
myVar = sharedResource 1
reader = () ->
myVar.read ->
# noone is writing to myVar while doing this request:
getDataFromServer1().then (data) ->
# read myVar.value instead of myVar, e.g.
expect(data == myVar.value)
writer = () ->
myVar.write ->
# noone reads or writes myVar while doing this request:
getDataFromServer2().then (data) ->
# change myVar.value instead of myVar, e.g.
myVar.value = data
Q.all([reader(), reader(), reader(), writer(), writer(), writer()]).done ->
console.log 'done'
This worked perfectly when I had only one sharedResource
. Here's where the problem occurs
myVar1 = sharedResource 1
myVar2 = sharedResource 2
action1 = () ->
myVar1.read ->
myVar2.write ->
getDataFromServer1().then (data) ->
myVar2.value = data + myVar1.value
action2 = () ->
myVar2.read ->
myvar1.write ->
getDataFromServer2().then (data) ->
myVar1.value = data + myVar2.value
Q.all([action1(), action1(), action1(), action2(), action2(), action2()]).done ->
console.log 'done'
Here a case of deadlock happens. Each promise is waiting for the other one to resolve. None of them get resolved and the program stops.
Edit
I'll try my best to explain:
This is actually code to test my server. To see how it performs when multiple clients send multiple requests at the same time. Say for example every time action1
sends a request, the server increments a value stored in it's database. On the client side (the code that you are seeing) I also increment a variable which contains the value I expect to be on the server. And then when action2
sends a request, the server responds with that value and I assert
the value in the response to be the same as my local variable.
So I have to get the lock before I send a request to make sure action2
doesn't ask for the variable while it is being changed.
I hope that helps.