# Concurrency Architecture

**URL:** <https://mediasoup.discourse.group/t/concurrency-architecture/2515>\
**Category:** mediasoup libraries\
**Created:** [February 26, 2021, 10:36pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515 "2021-02-26T22:36:03Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 26, 2021, 10:36pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/1 "2021-02-26T22:36:03Z")

</div>

Hi there. Had a question regarding mediasoup’s async design.

I accidently created what I think was a race condition while working with mediasoup-server where I called producer.pause/.resume followed by transport.pipeToRouter(producer) and transport.consume(producer) without first waiting for the .pause/.resume to complete, and ended up with producer.paused and consumer.producerPaused in inconsistent states. I fixed my code to use a mutex for the producer which sequenced these events and the problem went away.

**My question is how can I identify which mediasoup operations are not “concurrency safe” with each other and should be mutexed?** For example, can I call transport.consume asynchronously on the same transport for different producers? Same transport/same producer (one producer to many consumers)? _I’m concerned about a large performance penalty if I just locked the entire router anytime I had to make a change._

**Or is it some other problem with my code, and mediasoup’s architecture is fully threadsafe where it’s ok to do stuff like this:**

```auto
async function () {
  producer=await transport.produce 
  producer.resume <--no await here
  transport1.consume(producer) <--no await here
  transport2.consume(producer) <--no await here
  producer.pause <--no await here
  router1.pipeToRouter(producer,router2).then( <--no await here
     transport3.consume(producer_from_pipeToRouter)
  )
  producer.resume
}

```

Thanks a bunch!

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/nazar-pc/32/369_2.png) [@nazar-pc](https://mediasoup.discourse.group/u/nazar-pc)\
**Post date:** [February 26, 2021, 11:20pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/2 "2021-02-26T23:20:52Z")

</div>

I think you would generally want to have potentially racy things related to the same producer in some kind of producer-specific queue.

Though it sounds similar to [consumer not found Error](https://mediasoup.discourse.group/t/consumer-not-found-error/1774/) and should probably be handled nicely by mediasoup itself.

P.S. There is just one thread in JavaScript unless you are using workers explicitly, it is just asynchronous.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 27, 2021, 7:03pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/3 "2021-02-27T19:03:52Z")

</div>

I did a little more investigating and it looks like a race condition in mediasoup between

```auto
     producer.resume and router.pipeToRouter(producer ... )

```

If producer.resume is called between pipeToRouter’s start and completion, the pipeProducer’s state becomes inconsistent.

Cheers!

Experimental Data Below

```auto
WITHOUT producer MUTEX:
router1.pipeToRouter(producer , router2)
  State:
    producer.paused == true

  producer.Resume called.
  producer.Resume complete.
pipeToRouter Completed

```

States at this time:  
producer.paused == false  
pipeConsumer.producerPaused == false  
pipeConsumer.paused == false  
pipeProducer.paused == **true** ← Unexpected/inconsistent result

States (unchanged) after 2 seconds:  
producer.paused == false  
pipeConsumer.producerPaused == false  
pipeConsumer.paused == false  
pipeProducer.paused == **true** ← Unexpected/inconsistent result

```auto
WITH producer MUTEX:
router1.pipeToRouter(producer , router2)
  State:
  producer.paused == true
pipeToRouter Completed

```

States at this time:  
producer.paused == true  
pipeConsumer.producerPaused == true  
pipeConsumer.paused == false  
pipeProducer.paused == **true** ← Expected result

```auto
producer.Resume called.
producer.Resume complete.

```

States after 2 seconds:  
producer.paused == false  
pipeConsumer.producerPaused == false  
pipeConsumer.paused == false  
pipeProducer.paused == **false** ← Expected result

---

<div class="post-metadata">

**Author:** ![snnz](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/snnz/32/693_2.png) [@snnz](https://mediasoup.discourse.group/u/snnz)\
**Post date:** [February 28, 2021, 2:59am UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/4 "2021-02-28T02:59:05Z")

</div>

When you call `producer.resume`, the router sends ‘`producerresume`’ notification to all routers attached to the producer. Under the normal circumstances, pipeConsumer receives that notification and raises a ‘`producerresume`’ event itself and a ‘`resume`’ event in the observer. And there is a handler attached to the observer that resumes pipeProducer. But if you call `producer.resume` before the pipeToRouter (which includes a series of async calls) completes: 1) pipeConsumer may not even be created yet, or 2) the handler of the pipeConsumer’s observer ‘`resume`’ event may be not installed. There is no race condition: `pipeProducer.resume` is just never called at all. So, just wait until `pipeToRouter` is resolved before calling `pipeProducer.resume`.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 3:35am UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/5 "2021-02-28T03:35:06Z")

</div>

Yes! I was looking at the .produce call in pipeToRouter (below), and thinking if a pause/resume happens right after that it can end up in an inconsistent state like you stated.

```auto
				pipeProducer = await remotePipeTransport!.produce(
					{
						id : producer.id,
						kind : pipeConsumer!.kind,
						rtpParameters : pipeConsumer!.rtpParameters,
						paused : pipeConsumer!.producerPaused,
						appData : producer.appData
					});

				pipeConsumer!.observer.on('close', () => pipeProducer!.close());
				pipeConsumer!.observer.on('pause', () => pipeProducer!.pause());
				pipeConsumer!.observer.on('resume', () => pipeProducer!.resume());

```

I can’t control the producer pause/resume call timing in my application (since they’re user generated), but just mutexing those calls with pipeToRouter makes them get called in sequence and avoids the issue entirely.

Mediasoup is awesome.

---

<div class="post-metadata">

**Author:** ![snnz](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/snnz/32/693_2.png) [@snnz](https://mediasoup.discourse.group/u/snnz)\
**Post date:** [February 28, 2021, 8:42am UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/6 "2021-02-28T08:42:27Z")

</div>

Why would you need “mutexes” (whatever that means), if all this is Promise-based? await pipeToRouter (or pipeToRouter.then) is all that is neccessary.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 3:45pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/7 "2021-02-28T15:45:00Z")

</div>

For my use case the producer.pause/.resume occurs in a separate function from pipeToRouter. I have multiple people on a group call that can each pause/resume (mute/unmute) their own producers, and other people can join the call (which sometimes uses pipeToRouter). Since those are decoupled events, there’s no easy place to stick an ‘await’ or .then (hence the mutex usage).

‘Mutex’ is short for mutually exclusive; it’s a way to prevent two asynchronous events from happening at the same time:

[https://en.wikipedia.org/wiki/Mutual\_exclusion](https://en.wikipedia.org/wiki/Mutual_exclusion) - Concept  
[https://www.npmjs.com/package/async-mutex](https://www.npmjs.com/package/async-mutex) - Implementation in Node

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/nazar-pc/32/369_2.png) [@nazar-pc](https://mediasoup.discourse.group/u/nazar-pc)\
**Post date:** [February 28, 2021, 5:10pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/8 "2021-02-28T17:10:06Z")

</div>

You can pause producers client-side and no data will flow at all.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 5:33pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/9 "2021-02-28T17:33:01Z")

</div>

Thanks Nazar! I guess I’m getting tripped up because I feel like I need to mirror the client state on the server. Are you saying that I can just pause/resume the producer client side and there’s no need to signal the server?

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/nazar-pc/32/369_2.png) [@nazar-pc](https://mediasoup.discourse.group/u/nazar-pc)\
**Post date:** [February 28, 2021, 5:40pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/10 "2021-02-28T17:40:13Z")

</div>

Yep, data will just stop flowing until you resume.  
You may need to signal this if you care to know about this on other clients (for instance stats will go down as the result of this), but other than that - no.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 5:43pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/11 "2021-02-28T17:43:40Z")

</div>

I guess the natural follow up question is:

If I want to stop RTP from flowing to a specific consumer from an active producer, I just call consumer.pause server side, and don’t bother notifying the client on the consuming end?

Is there ever a reason to call consumer.pause on the client side? Seems like this is a waste of bandwidth because the server would still send RTP if the producer is active.

I got tripped up by this because when I read the documentation I interpreted it as if you call pause on one side (client/server) you gotta call it in the other side too. What you’re saying is that’s not necessary, and I would just use signaling for my own non-mediasoup purposes.

---

<div class="post-metadata">

**Author:** ![nazar-pc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/nazar-pc/32/369_2.png) [@nazar-pc](https://mediasoup.discourse.group/u/nazar-pc)\
**Post date:** [February 28, 2021, 6:15pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/12 "2021-02-28T18:15:58Z")

</div>

Well, anything signaling-related you need to implement yourself, but with client-side producer you don’t necessarily need to signal it since browser just stops sending data without any warnings. Depending on use case this could be sufficient on its own.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 6:41pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/13 "2021-02-28T18:41:57Z")

</div>

Thanks @nazar-pc and @snnz !

---

<div class="post-metadata">

**Author:** ![snnz](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/snnz/32/693_2.png) [@snnz](https://mediasoup.discourse.group/u/snnz)\
**Post date:** [February 28, 2021, 8:22pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/14 "2021-02-28T20:22:40Z")

</div>

> [@dimoochka](#):
>
> Since those are decoupled events, there’s no easy place to stick an ‘await’ or .then (hence the mutex usage).

Ok, your point is well taken. Creation of the pipeProducer (with the initial state taken from the pipeConsumer at some point) and propagation of the pause/resume from the producer to pipeProducer are by far not the atomic operations and should be prevented from overlapping one way or another.

I know, of course, what mutex is as a concept. I just meant that since there was no such a thing as native mutexes in JS, it could denote different things. But usually it amounts to building a queue of Promises, as aforementioned async-mutex or awaitqueue in mediasoup do.

> [@dimoochka](#):
>
> Is there ever a reason to call consumer.pause on the client side?

It effectively just sets the received track.enabled to false. One might probably call it to temporarily mute received media.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [February 28, 2021, 8:29pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/15 "2021-02-28T20:29:25Z")

</div>

> [@snnz](#):
>
> I know, of course, what mutex is as a concept. I just meant that since there was no such a thing as native mutexes in JS, it could denote different things. But usually it amounts to building a queue of Promises, as aforementioned async-mutex or awaitqueue in mediasoup do.

Sorry - I misunderstood! Yeah, my initial workaround was a queue of promises (not a mutex in the literal sense since it’s not threaded code).

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 1, 2021, 1:45pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/16 "2021-03-01T13:45:19Z")

</div>

Hi, if there is inconsistences in the pipe Producer state this is a bug. Would it be possible to have a test code that reproduces it 100% of times? If so, please report it into a new issue in GitHub.

---

<div class="post-metadata">

**Author:** ![dimoochka](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/dimoochka/32/858_2.png) [@dimoochka](https://mediasoup.discourse.group/u/dimoochka)\
**Post date:** [March 1, 2021, 2:26pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/17 "2021-03-01T14:26:40Z")

</div>

@ibc Sure, I’ll try to put together some simple test code to reproduce it this evening. It seemed consistently reproducible in my dev environment.

---

<div class="post-metadata">

**Author:** ![snnz](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/snnz/32/693_2.png) [@snnz](https://mediasoup.discourse.group/u/snnz)\
**Post date:** [March 1, 2021, 2:58pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/18 "2021-03-01T14:58:47Z")

</div>

router.pipeToRouter is divided into several async operations that give opportunity for other code to run. For example:

```auto
				pipeConsumer = await localPipeTransport!.consume(
					{
						producerId : producerId!
					});

				pipeProducer = await remotePipeTransport!.produce(
					{
						id : producer.id,
						kind : pipeConsumer!.kind,
						rtpParameters : pipeConsumer!.rtpParameters,
						paused : pipeConsumer!.producerPaused,
						appData : producer.appData
					});

```

Suppose that while it awaits the result of the first call, `producer.resume` is called somewhere else. Then the pipeConsumer will be created, and the request to create the pipeProducer will be placed in the queue with the initial value for `paused` taken from the `pipeConsumer.producerPaused`. But `producer.resume` request will be handled before it, `producer.paused` and `pipeConsumer.producerPaused` will change, while the pipeProducer is not created yet.

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 1, 2021, 3:07pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/19 "2021-03-01T15:07:00Z")

</div>

In theory the only problem here would be if `producer.pause()` is called **while** `pipeProducer = await remotePipeTransport!.produce()` is in progress, right?

It looks to me that the only that we must do is:

Once the `pipeProducer` is created, check the `paused` status of the original `producer` and, if different, call `await pause()/resume()` in `pipeProducer` before the whole function resolves. Agreed?

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 1, 2021, 3:15pm UTC](https://mediasoup.discourse.group/t/concurrency-architecture/2515/20 "2021-03-01T15:15:22Z")

</div>

It may/should be fixed here in the `v3` branch: [router.pipeToRouter(): Fix possible inconsistency in pipeProducer.pau… · versatica/mediasoup@569d135 · GitHub](https://github.com/versatica/mediasoup/commit/569d135cab7537c205ec64a8813d4900e395d368)

[Next page](https://mediasoup.discourse.group/t/concurrency-architecture/2515.md?page=2)
