Sistemas Operativos Sincronização de threads com objectos kernel no Windows Centro de Cálculo Instituto Superior de Engenharia de Lisboa João Pedro Patriarca (jpatri@cc.isel.ipl.pt)
Objectos Kernel Processes Threads Jobs File and console standard input/output/error streams Events Waitable timers Semaphores Mutexes Objectos usados para sincronização de threads Um objecto pode estar sinalizado ou não Uma thread pode-se colocar no estado Wait até que um objecto fique sinalizado As regras de sinalização dos objectos dependem do tipo concreto de objecto: ex, processos e threads sinalizados indicam a sua terminação; uma vez atingido esse estado, não mais sairão Um objecto kernel é partilhável por vários processos
Funções de espera (Wait) de sinalização de objectos DWORD WaitForSingleObject( HANDLE hobject, DWORD dwmilliseconds); dwmilliseconds = INFINITE = 0xFFFFFFFF (-1) Retorna WAIT_FAILED, WAIT_TIMEOUT, WAIT_OBJECT_0 Esperar por um objecto sinalizado promove o retorno imediato DWORD WaitForMultipleObjects( DWORD dwcount, CONST HANDLE * phobjects, BOOL bwaitall, DWORD dwmilliseconds); bwaitall: true = espera por todos No caso de sucesso, retorna WAIT_OBJECT_0 + N, com N igual a 0 se bwaitall a true com N igual ao valor do índice do objecto sinalizado se bwaitall a false; especial atenção ao objecto sinalizado se pretender-se chamar a função de espera novamente
Consequências da espera por sinalização de objectos A espera com sucesso por sinalização em alguns objectos promove a alteração de estado nos próprios objectos Um retorno sem sucesso nunca altera o estado do objecto A função WaitForMultipleObjects garante atomicidade na alteração de estado dos objectos pelos quais espera sinalização (no cenário do parâmetro bwaitall vir a true) Quando o objecto kernel não for mais necessário dever-se-á chamar a função BOOL CloseHandle(HANDLE hobject);
Objecto kernel Event O objecto mais simples de todos Contém um contador de referências (tal como todos os objectos kernel) booleano que indica se o reset ao evento é feito automaticamente ou manualmente quando sinalizado booleano que indica se o estado do evento é sinalizado ou não Utilização adequada na cooperação entre threads: O trabalho a realizar por uma thread depende do trabalho realizado por outra thread Todas as threads na lista de waitables ficam schedulable quando um evento com reset manual é sinalizado Apenas uma thread na lista de waitables fica schedulable quando um evento com reset automático é sinalizado
Objecto kernel Event Funções relevantes http://msdn.microsoft.com/en-us/ library/ms686670(v=vs.85).aspx
Waitable timer kernel objects HANDLE CreateWaitableTimer( PSECURITY_ATTRIBUTES psa, BOOL bmanualreset, PCTSTR pszname); HANDLE OpenWaitableTime( DWORD dwdesiredaccess, BOOL binherithandle, PCTSTR pszname); BOOL SetWaitableTimer( HANDLE htimer, const LARGE_INTEGER *pduetime, LONG lperiod, PTIMEAPCROUTINE pfncompletionroutine, PVOID pvargtocompletionroutine, BOOL bresume); Normalmente a false BOOL CancelWaitableTimer(HANDLE htimer); >= 0, data absoluta < 0, tempo relativo à chamada da função Asynchronous Procedure Call Normalmente a NULL
Objecto kernel Semaphore Adequado para contador de recursos Para além dos atributos comuns de um objecto kernel, contém 2 valores a 32 bits com sinal contador com o número máximo de recursos que o semáforo pode controlar contador com o número corrente de recursos disponíveis Regras de sinalização contador corrente de recursos maior que zero, o semáforo está sinalizado contador corrente de recursos igual a zero, o semáforo não está sinalizado o sistema nunca permite que o contador corrente de recursos seja negativo o valor do contador corrente de recursos não pode ser nunca maior do que o valor do contador com o número máximo de recursos
Objecto kernel Semaphore Funções relevantes HANDLE CreateSemaphore( PSECURITY_ATTRIBUTES psa, LONG linitialcount, LONG lmaximumcount, PCTSTR pszname); HANDLE CreateSemaphoreEx( PSECURITY_ATTRIBUTES psa, LONG linitialcount, LONG lmaximumcount, PCTSTR pszname, DWORD dwflags, DWORD dwdesiredaccess); HANDLE OpenSemaphore( DWORD dwdesiredaccess, BOOL binherit, PCTSTR pszname); Uma thread que solicite um recurso com o contador de recursos corrente a zero é colocada no estado Wait O teste e decremento do contador corrente de recursos é feito de forma atómica BOOL ReleaseSemaphore( HANDLE hsemaphore, LONG lreleasecount, PLONG plpreviouscount); Aceita o argumento NULL Indica quantos recursos estão disponíveis inicialmente Reservado, deverá ser 0
Objecto kernel Mutex Garante exclusão no acesso a um recurso Para além dos atributos comuns de um objecto kernel, contém identificador da thread que detém o mutex um contador de recursões Regras de sinalização Se o identificador da thread for 0, o mutex não é detido por nenhuma thread e, portanto, está sinalizado Se o identificador da thread for diferente de 0, o mutex é detido por uma thread e, portanto, não está sinalizado Mesmo sem estar sinalizado, o acesso a recursos guardados pelo mutex pode ser concedido num caso especial: a thread que solicita o recurso é a mesma que já o detém
Objecto kernel Mutex Funções relevantes 0 : false CREATE_MUTEX_INITIAL_OWNER : true HANDLE CreateMutex( PSECURITY_ATTRIBUTES psa, BOOL binitialowner, PCTSTR pszname); HANDLE CreateMutexEx( PSECURITY_ATTRIBUTES psa, PCTSTR pszname, DWORD dwflags, DWORD dwdesiredaccess); HANDLE OpenMutex( DWORD dwdesiredaccess, BOOL binheritowner, PCTSTR pszname); Uma thread é colocada no estado Wait quando solicita o recurso guardado pelo mutex se o identificador da thread for diferente de 0 e diferente da thread que solicita o recurso O teste do identificador e incremento do contador de recursões é feito de forma atómica BOOL ReleaseMutex(HANDLE hmutex); Decrementa o contador de recursões se o identificador da thread for igual à thread correntte O mutex fica sinalizado se o contador de recursões atingir o valor 0 Uma função de espera retorna o valor especial WAIT_ABANDONED caso a thread que detém o mutex termine sem o libertar (o SO detecta-o colocando o identificador da thread e o contador de recursões a 0)
Resumo bib: Windows via C/C++, Richter, 5 edition, Tab. 9-3 Object When nonsignaled When signaled Successful Wait Side Effect Process When still alive When terminates (ExitProcess, TerminateProcess) Thread When still alive When terminates (ExitProcess, TerminateProcess) Job File When time s job has not expired When I/O request is pending When time job expires When I/O request completes None None None None Console input No input exists When input is available None File change notifications No file have changed When fyle system detects changes Reset notification
Resumo (continuação) Object When nonsignaled When signaled Successful Wait Side Effect Auto-reset event Manual-reset event Auto-reset waitable timer Manual-reset waitable timer ResetEvent, PulseEvent or successful wait ResetEvent or PulseEvent CancelWaitableTimer or successful wait CancelWaitableTimer When SetEvent or PulseEvent is called When SetEvent or PulseEvent is called When time comes due (SetWaitableTimer) When time comes due (SetWaitableTimer) Semaphore Successful wait When count > 0 (ReleaseSemaphore) Mutex Successful wait When unowned by a thread (ReleaseMutex) Reset event None Reset timer None Decrement count by 1 Give ownership to a thread
Resumo (continuação) Object When nonsignaled When signaled Successful Wait Side Effect CriticalSection (usermode) SWRLock (user-mode) Condition variable (user-mode) Successful wait ((Try) Enter-Critical-Section) Successful wait (AquireSRWLock (Exclusive))) Successful wait (SleepConditionVariable ) When unowned by a thread (LeaveCriticalSection) When unowned by a thread (ReleaseSRWLock (Exclusive)) When woken up (Wake (All)ConditionVariable) Give onweship to a thread Gives onweship to a thread None
Bibliografia Windows via C/C++, Richter e Nasarre, cap. 9, 5ª edição MSDN windows development