Difference between revisions of "Implementing a Mutex Pool"

From AOLserver Wiki
Jump to navigation Jump to search
m (remove spam)
 
Line 1: Line 1:
The changing faces of Technology:
+
In a multi-threaded application, there may be a need to "dynamically" create mutexes for short-lived operations and the natural desire to destroy them once you are done.  Creating and destroying mutexes may be a relatively cheap operation, it's still not the best way to do this.  Instead, we can simply implement mutex pools and allocate mutexes out of it.
  
A category of hardware and software that enables people to use the Internet as the transmission medium for telephone calls. For users who have free, or fixed-price Internet access, Internet telephony software essentially provides free telephone calls anywhere in the world. To date, however, Internet telephony does not offer the same quality of telephone service as direct telephone connections.  
+
----
There are many Internet telephony applications available. Some, like CoolTalk and NetMeeting, come bundled with popular Web browsers. Others are stand-alone products. Internet telephony products are sometimes called IP telephony, Voice over the Internet (VOI) or Voice over IP (VOIP) products.
+
Note that this is ''not'' just about performance.  Because of the way
 +
the underlying POSIX thread API works, there are strong correctness
 +
reasons for avoiding the regular use of "ns_mutex destroy".
 +
TODO:  Add more details about that.
 +
--[[atp]]@piskorski.com, 2004/05/28 19:02 EDT
 +
----
  
 +
First, we have to determine the size of our mutex pool.  We must decide that "we will never handle more than X concurrent requests", where X may be 1000.  At server start-up, create 1001 mutexes.  Store 1000 of them in a single NSV, where the key is the mutex ID, value an empty string.  Store the 1001th mutex in another NSV, with the key being some constant string and the mutex ID being the value.  The code may look something like this:
  
Interent Technology Links:
+
  nsv_set guardMutex guard [[ns_mutex create]]
* [http://www.classytattoos.com/star-tattoos.php Star Tattoos]                  
+
  for {set i 0} {$i < 1000} {incr i} {
* [http://www.classytattoos.com/tribal-tattoos.php Tribal Tattoos]                    
+
    nsv_set mutexPool [[ns_mutex create]] {}
* [http://www.classytattoos.com/celebrity-tattoos.php Celebrity Tattoos]                   
+
  }
* [http://www.classytattoos.com/butterfly-tattoos.php Butterfly Tattoos]                    
+
 
* [http://www.classytattoos.com/tattoo-removal.php Tattoo Remova]                    
+
Whenever you need to grab a mutex, you use the NSV to get the mutex ID of that 1001th (guard) mutex, and lock it. Then, you pop a mutex out of the pool, then release the guard mutex. You can then use that popped mutex for the particular request you want to perform:
* [http://www.classytattoos.com/tattoos.php Tattoos]                    
+
 
* [http://www.dog-guide.org/ Dogs]                   
+
  ns_mutex lock [[nsv_get guardMutex guard]]
* [http://www.dog-guide.org/dog-breeds.php Dog Breeds]                   
+
  set mutex [[lindex [nsv_array names mutexPool]] 0]
* [http://www.dog-guide.org/dog-names.php Dog Names]                    
+
  nsv_unset mutexPool $mutex
* [http://www.dog-guide.org/allergies-in-dogs.php Allergies in Dogs]                   
+
  ns_mutex unlock [[nsv_get guardMutex guard]]
* [http://www.saunaguide.org/ Sauna]                   
+
 
* [http://www.saunaguide.org/home-sauna.php Home Sauna]                   
+
(Obviously, you want to add some error checking to make sure that [llength [[nsv_array names mutexPool]]] is not 0 -- in which case, you've exhausted your mutex pool and should either spinwait for a mutex to be returned to the pool, or handle the error however appropriate.)
* [http://www.saunaguide.org/infra-red-sauna.php Infra Red Sauna]                    
+
 
* [http://www.saunaguide.org/sauna-benefits.php Sauna Benefits]                  
+
When the request is done and you're done with the mutex, instead of destroying it, return it to the pool:
* [http://www.saunaguide.org/sauna-rooms.php Sauna Rooms]                   
+
 
* [http://www.teainfo.org/ Tea]                   
+
  ns_mutex lock [[nsv_get guardMutex guard]]
* [http://www.teainfo.org/green-tea.php Green Tea]                    
+
  nsv_set mutexPool $mutex {}
* [http://www.teainfo.org/tea-gardner.php Tea Gardner]  
+
  ns_mutex unlock [[nsv_get guardMutex guard]]
* [http://www.voip-guide.org/ voip]
+
 
* [http://www.voip-guide.org/voip.php voip]
+
No destroying of mutexes. Except, at server shutdown, you may want to grab the guard mutex and foreach over all the mutexes in mutexPool and destroy them all. You might want to check the size of the pool and if it's not 1000, you know that not all mutexes have been returned to the pool and you can handle that however you please.
* [http://www.voip-guide.org/voip-services.php VoIP Services]
+
 
* [http://www.voip-guide.org/voip-provider.php VoIP Provider]
+
Doing this, we no longer have to create and destroy mutexes all the time. We serialize the mutex pool through a single global guard mutex, but that's not as bad as constantly creating and destroying mutexes.

Latest revision as of 17:49, 21 December 2005

In a multi-threaded application, there may be a need to "dynamically" create mutexes for short-lived operations and the natural desire to destroy them once you are done. Creating and destroying mutexes may be a relatively cheap operation, it's still not the best way to do this. Instead, we can simply implement mutex pools and allocate mutexes out of it.


Note that this is not just about performance. Because of the way the underlying POSIX thread API works, there are strong correctness reasons for avoiding the regular use of "ns_mutex destroy". TODO: Add more details about that. --atp@piskorski.com, 2004/05/28 19:02 EDT


First, we have to determine the size of our mutex pool. We must decide that "we will never handle more than X concurrent requests", where X may be 1000. At server start-up, create 1001 mutexes. Store 1000 of them in a single NSV, where the key is the mutex ID, value an empty string. Store the 1001th mutex in another NSV, with the key being some constant string and the mutex ID being the value. The code may look something like this:

 nsv_set guardMutex guard ns_mutex create
 for {set i 0} {$i < 1000} {incr i} {
   nsv_set mutexPool ns_mutex create {}
 }

Whenever you need to grab a mutex, you use the NSV to get the mutex ID of that 1001th (guard) mutex, and lock it. Then, you pop a mutex out of the pool, then release the guard mutex. You can then use that popped mutex for the particular request you want to perform:

 ns_mutex lock nsv_get guardMutex guard
 set mutex [[lindex [nsv_array names mutexPool]] 0]
 nsv_unset mutexPool $mutex
 ns_mutex unlock nsv_get guardMutex guard

(Obviously, you want to add some error checking to make sure that [llength nsv_array names mutexPool] is not 0 -- in which case, you've exhausted your mutex pool and should either spinwait for a mutex to be returned to the pool, or handle the error however appropriate.)

When the request is done and you're done with the mutex, instead of destroying it, return it to the pool:

 ns_mutex lock nsv_get guardMutex guard
 nsv_set mutexPool $mutex {}
 ns_mutex unlock nsv_get guardMutex guard

No destroying of mutexes. Except, at server shutdown, you may want to grab the guard mutex and foreach over all the mutexes in mutexPool and destroy them all. You might want to check the size of the pool and if it's not 1000, you know that not all mutexes have been returned to the pool and you can handle that however you please.

Doing this, we no longer have to create and destroy mutexes all the time. We serialize the mutex pool through a single global guard mutex, but that's not as bad as constantly creating and destroying mutexes.