Semaphores
Semaphores are one of several ways to control access to a data structure or piece of code, and is often times seen in conjunction with a mutex or condition variable. Semaphores are like elevators to a certain extent ; an elevator can only hold so many people as per its limit. After a certain amount of people enter the elevator, no more people can enter. A semaphore acts in a similar way, but with threads (as opposed to people). You can setup how many threads can enter, before it begins to deny entry and wait until some threads leave to begin accepting requests again, like my friend Gandalf up there :) Hopefully now you understand the gif!
The C standard library provides a very compact way of using semaphores. Functions dealing with semaphores have a sem_ prefix. And the data type for a semaphore is a sem_t. It's also easy enough to import, #include<semaphore.h>. However, the purpose of this blog entry is to explain how to setup semaphores, using the C standard library, under Mac OS X. So this begs the question, how are semaphores under Mac different than in Linux? ( I can't make a claim for Windows).
Most tutorials online for setting up semaphores under Linux make use of something called unnamed semaphores, which are exactly as it sounds -- Semaphores that have no named identification. Semaphores that are unnamed are usually located in an agreed upon memory location so that other threads can access this semaphore, shared memory, or stored on the heap. However, Mac OS X does not make use of this unnamed semaphore scheme, and if you've ever developed a threaded application for Mac OS X, you'd be surprised to know this compiles and would appear to work, but fails miserably:
int main()
{
sem_t semaphore;
sem_init(&semaphore,0,0);
// begin using the semaphore
// call sem_wait() to block until a sem_post() from a different thread
}
If on a separate thread you're sem_wait()'ing, the unnamed semaphore will not block and the supposedly waiting thread will actually start executing, even if the semaphore count is 0!! This bug is usually pretty tough to catch, unless you analyze the return value of the call to sem_init(). It returns -1 on error, and sets errno appropriately. If you do happen to spin up this code:
#include <semaphore.h>
#include <iostream>
int main()
{
sem_t semaphore;
int retVal = sem_init(&semaphore,0,0);
if(retVal == -1)
{
perror("Failed to initialize semaphore");
}
return 0;
}
You'll get this wonderful error message:
Function not implemented
Which can puzzle many a concurrent coder. Mac OS X ( as far as version Mavericks), does not support unnamed semaphores, but instead requires you to use a named semaphore. A named semaphore allows for better IPC between two otherwise unrelated threads, since the semaphore has a distinct name ( which is actually a path internally). For more information about named vs unnamed semaphores, view this stackoverflow link,( http://stackoverflow.com/questions/13145885/name-and-unnamed-semaphore). Creating a named semaphore has a different procedure for initializing and destructing it, but the functionality is otherwise the same, except for the fact that you'll be dealing with a sem_t pointer (sem_t*) as opposed to a regular sem_t.
A named semaphore, as you may have intelligently guessed, requires a name upon initialization. Unfortunately, sem_init does not have a parameter for const char*, you'll need to use another function, sem_open. The fine-grain details for using sem_open are highlighted within the man pages, but there is an overload for that function.
- sem_open(const char* name, int oflag)
- sem_open(const char* name, int oflag, mode_t mode, unsigned int value)
The common parameters between those two functions, namely const char* name and int oflag specify the name that you wish to give the semaphore. Note, there is a special "syntax" for the naming conventions of semaphores, which you can find in this link. oflag specifies bitwise flags that control the operation of the creating of the named semaphore ( all the flags can be found in <fcntl.h>). The two main ones you'll encounter will be O_CREAT and O_EXCL. The former tells the function to actually create the named semaphore, if it does not already exist. O_EXCL is more of a "companion" flag, that you'll usually end up OR-ing it with O_CREAT to specify exclusivity. If used in conjunction with O_CREAT, the function call will fail if the named semaphore already exists. Again, the details can be read in the man pages, but to put it into context, these flags are the same as when using open().
Specifically in the overload, mode_t refers to the permissions given to the class of users regarding access to this semaphore. The actual flags can be found in <sys/stat.h> but if you're savvy with chmod, the permission syntax is the same ( it uses octal as its counting system and specifies permissions for user,group, and others).
And perhaps the most trivial to understand parameter, value, specifies the initial value of the semaphore ( how many threads can sem_wait() and get accepted at a time).
A sample named semaphore can be spun up like this:
#include <iostream>
#include <fcntl.h>
int main()
{
sem_t* semaphore = sem_init("/my_semaphore",O_CREAT|O_EXCL,0666,2); // notice how now I am using a sem_t pointer
}
Now, I have a pointer to a semaphore! Errors can happen here as well, such as if the name "/my_semaphore" is already taken (since we specified to fail with the O_EXCL flag). In the case of an error, sem_init will return a constant SEM_FAILED. Proper error handling would include checking if semaphore == SEM_FAILED
If this part clears, then the rest of the semaphore usage is the same old song and dance as an unnamed semaphore (until we get to the destruction phase). I'll assume you know how to use the sem_post and sem_wait functions, as they are the same with named and unnamed semaphores, except that with unnamed semaphores you'll have to pass a reference to your sem_t object into each one of the functions, since its expecting a pointer, but lucky for us, using named semaphores provides us with pointers :)
Destruction of a named semaphore requires sem_close()'ing and sem_unlink()'ing it, since in a way, its sort of like an open file.
sem_close(semaphore);
sem_unlink("/my_semaphore");
The call to sem_close() essentially states that the semaphore is to no longer be used. It is in someways analogous to sem_destroy() for unnamed semaphores. sem_unlink() however, takes a const char* to the name of the named semaphore. If done successfully, it removes/dissociates the semaphore from that name.
I hope this gentle introduction to semaphores on Mac OS X has proved helpful! My next few blog posts will be about more concurrency utilities and methods, all leading up to a big secret project that I'll release shortly as an open source project! :)
The elevator access is ideal for mulch-tenant premises since it restricts access to floors that you have no business on. This is good in terms of security and the
ReplyDeleteconvenience it gives to the tenants. There are two types of controllers you can choose from there is the 1 cab and the 2 cab which can greatly make a difference on your facility.
elevator access control
Elevator access can rely on upon the client's order, the season of day, which elevator is being drawn nearer, and the equipped condition of an office. What's more, changes to the status of a zone can either make lift stipend access to a story, or reason the lift to deny access. By utilizing one or a greater amount of these qualities, you can make lift access control strategies that are to a great degree adaptable and secure. By utilizing a lift access control framework, you can isolate the overall population and unapproved clients, from clients, from approved clients.
ReplyDeleteelevator access control