0

I am working on a Resource Manager and some jobs must be atomic, because there may be race condition.

I see a solution is provided in a similar post Javascript semaphore / test-and-set / lock?

which says implement a basic integer semaphore, just add the variable into the DOM and lock/unlock it and make sure your functions keep checking it, else timeout them

Can someone help me how can I implement it for below code ? and How can I add a variable to DOM ?

In the code below retrievePartners methods returns partners based on what user asked in capabilities object, and retrievePartners method may have been called same time from another user, asking for same partners (browsers.) So this method should be atomic, means should deal only with one capabilities at a same time.

  async retrievePartners (capabilities) {
    const appropriatePartners = { chrome: [], firefox: [], safari: [], ie: [] }
    const partners = await this.getAllPartners()
    allTypeNumber = 0
    // first check if there is available appropriate Partners
    Object.keys(capabilities.type).forEach(key => {
      let typeNumber = parseInt(capabilities.type[key])
      allTypeNumber = allTypeNumber + typeNumber
      for (let i = 0; i < typeNumber; i++) {
        partners.forEach((partner, i) => {
          if (
            key === partner.value.type &&
            partner.value.isAvailable &&
            appropriatePartners[key].length < typeNumber
          ) {
            appropriatePartners[key].push(partner)
          }
        })

        if (appropriatePartners[key].length < typeNumber) {
          throw new Error(
            'Sorry there are no appropriate Partners for this session'
          )
        }
      }
    })

    if (allTypeNumber === 0) {
      throw new Error('Please mention at least 1 type of browser !')
    } else {
      Object.keys(appropriatePartners).forEach(key => {
        appropriatePartners[key].forEach(partner => {
          this.instructorPeer.set('/partners/' + partner.id + '/states/', {
            isAvailable: false
          })
        })
      })

      return appropriatePartners
    }
  }

getAllPartners method

  async getAllPartners (capabilities) {
    const partners = []
    const paths = await this.instructorPeer.get({
      path: { startsWith: '/partners/' }
    })
    paths.forEach((path, i) => {
      if (path.fetchOnly) {
        let obj = {}
        obj.value = path.value
        obj.id = path.path.split('/partners/')[1]
        obj.value.isAvailable = paths[i + 1].value.isAvailable
        partners.push(obj)
      }
    })
    return partners
  }

Here is how I call the method

async function startTest () {
  const capabilities = {
    type: {
      chrome: 1
    }
  }

  test('Example test', async t => {
    try {
      session = await workout.createSession(capabilities)
      const partners = session.partners
      const partner = partners.chrome[0]
...
Rasim Avcı
  • 1,123
  • 2
  • 10
  • 16
  • "*retrievePartners method may have been called same time from another user. So this method should be atomic, means should deal only with one capabilities at a same time.*" Why? There's no reason to introduce locking here, is there? Just keep all the state local to each call. – Bergi Apr 05 '18 at 13:40
  • browsers are limited and during for loop, some other test may have already taken it ? btw, what you mean by keeping all the state local to each call.. Can you write some example code? – Rasim Avcı Apr 05 '18 at 14:11
  • No, no two `for` loops will run at the same time (unless there's an `await` inside them). – Bergi Apr 05 '18 at 14:26
  • I had assumed that `retrievePartners` would just retrieve appropriate partners fitting the capabilities. I would think `await getAllPartners()` could run multiple times concurrently without affecting each other. To keep state local, do not use a global `allTypeNumber` variable, and drop the `instructorPeer.set` call (which sounds very much like a side effect, a store to a database or so, nothing that a "retrieve" function should do). Of course yes, you would need to lock multiple stores against each other. – Bergi Apr 05 '18 at 14:29
  • Thanks Bergi but I really need more information. btw, instructorPeer is for Node-jet , a message broker. getAllPartners just returns partners (browsers) on the system. what you mean -> need to lock multiple stores against each other. Can you provide some code for all these , please – Rasim Avcı Apr 05 '18 at 14:38
  • I think you basically want to ensure that all the availabilities are updated and the next call would retrieve the new values, is that right? If so, you will need a read/write lock on the data. But you would also need to show us how you can know when `instructorPeer.set` has finished updating, in your current code it doesn't even look like an asynchronous call. – Bergi Apr 05 '18 at 14:51
  • Yes its right, Instructor.jet is just a method from this library https://github.com/lipp/node-jet . It makes that partner unavailable until some other function makes it available again. we can think time of set method is very very short,its just sending a JSON RPC message. we dont need to count it, and I need to make atomic all code right not just the set method ?. Meanwhile I never implemented lock mechanism, can you write some piece of code ? – Rasim Avcı Apr 05 '18 at 15:17
  • Btw, set method returns a promise , I can use an await for it.. – Rasim Avcı Apr 05 '18 at 15:22
  • A lock would be just as simple as a global boolean flag that you test and set when you enter the locked section and unset when you leave it. – Bergi Apr 05 '18 at 16:15
  • I don't know Jet, it mentions some kind of in-memory database. How does `this.getAllPartners()` access the value that was sent before? Would `set` make the new value available immediately, is it essentially synchronous? – Bergi Apr 05 '18 at 16:18
  • Yes , a kind of memory, partners holding states. Lets say its async, then how would your code be ? getAllPartners gets value from states. Partners are independent piece of code connect to system via websocket, each on a different browser. You can see set method in this link (at bottom of the page) https://github.com/lipp/node-jet/blob/master/lib/jet/peer.js – Rasim Avcı Apr 05 '18 at 18:41
  • I have been told that there is no atomic operations in JavaScript and you shouldn't need a library,I am confused – Rasim Avcı Apr 05 '18 at 18:43
  • All synchronous operations are "atomic" by default in JS, you don't need anything special (ignoring web workers with shared memory). All synchronous code runs to completion without anything else intercepting it. – Bergi Apr 05 '18 at 18:50
  • so you still dont have an answer but just comments ? I have been told that mutex-promise library is an antipattner, javascript doesnt need that. So, how can I make it atomic with native javascript ? in order to make atomic, all funtions should be synchronous right, since javascript goes one by one, but for async operations ones await is not enough ? – Rasim Avcı Apr 05 '18 at 20:08
  • Yes, I still don't have an answer as I still don't completely understand your requirements. Can it actually happen at all that `Promise.all([instructorPeer.set("/partners/1", {avail: true}), instructorPeer.get("/partners/1")]).then([_, {avail}] => avail == false)`, or does node-jet implicitly prevent that? – Bergi Apr 06 '18 at 07:51
  • I dont know but there is no need to get function. Set function resturns a promise. So lets say that retrievepartners method runs in server , how can I implement queue function and only execute the code once at a time. I have been tolda that javascript doesnt need a mutex or lock librarysince its single thread. – Rasim Avcı Apr 06 '18 at 11:37
  • Have a look at [this queue implementation](https://stackoverflow.com/a/47157577/1048572) or [this simple queuing](https://stackoverflow.com/a/32606544/1048572) for examples. You will need to use some kind of locking, but you can do it with simple variables and assignments, no need for special atomic operations or libraries. – Bergi Apr 06 '18 at 12:33
  • But how can I queue a function calling ? not a request pr sth like that. Can I queue a function called everytime ? – Rasim Avcı Apr 06 '18 at 12:36
  • See the `save` function in the second example, where `writeFile` is the promise-returning function to be called synchronised. You'll need to put a wrapper around your `retrievePartners` functionality. – Bergi Apr 06 '18 at 12:53
  • How to put that wrapper ? Can you provide some code ? – Rasim Avcı Apr 17 '18 at 08:37

0 Answers0