// throws ThreadInterruptedException public void DownWrite() { lock (lockObj) { //Se não houver leitores/escritores à espera e não houver nenhum escritor a escrever e leitores a ler if (rwq.Count == 0 && !isWriting && readersReading == 0) { //Um escritor a escrever isWriting = true; writerInside = Thread.CurrentThread.ManagedThreadId; return; } //Adicionar um novo escritor à lista de espera LinkedListNode <Rw> wr = rwq.AddLast(new Rw(WRITER)); do { try { //Espera SyncUtils.Wait(lockObj, wr); } catch (ThreadInterruptedException) { //Remover da lista de espera rwq.Remove(wr); //Lançar excepção throw; } //Se for o primeiro da lista e não houver ninguém a escrever nem a ler if (rwq.First == wr && !isWriting && readersReading == 0) { //Remover da lista de espera rwq.Remove(wr); //Escritor a escrever isWriting = true; writerInside = Thread.CurrentThread.ManagedThreadId; return; } } while (true); } }
// throws ThreadInterruptedException public void DownRead() { lock (lockObj){ //Se não houver escritores à espera, nem estiver nenhum escritor a escrever if (!isWriting && rwq.First.Value.rw != WRITER) { //mais um leitor a ler readersReading++; readersInside.AddLast(Thread.CurrentThread.ManagedThreadId); return; } //Adicionar um novo leitor à lista de espera LinkedListNode <Rw> rd = rwq.AddLast(new Rw(READER)); do { try { //Esperar SyncUtils.Wait(lockObj, rd); } catch (ThreadInterruptedException) { //Remover da lista de espera rwq.Remove(rd); //Lançar excepção throw; } //Pode ler se ninguem estiver a escrever e se for o primeiro da lista de espera if (!isWriting && rwq.First == rd) { //Remover da lista de espera rwq.Remove(rd); //Mais um leitor a ler readersReading++; readersInside.AddLast(Thread.CurrentThread.ManagedThreadId); return; } } while (true); } }
// throws ThreadInterruptedException public bool TryEnter(int key) { lock (map) { //Se não existir a key, adiciona-se essa key com uma nova região if (!map.ContainsKey(key)) { map.Add(key, new Region()); } //região associada à key Region r = map[key]; //se a região não estiver cheia a thread pode entrar if (r.inside < maxInside) { //mais uma thread dentro da região r.inside++; //conseguiu entrar logo retorna true return(true); } //se a lista de espera para entrar na região não estiver cheia a thread pode ficar à espera if (r.waiting.Count < maxWaiting) { //Momento inicial da espera int lastTime = (waitTimeout != Timeout.Infinite) ? Environment.TickCount : 0; //Nova thread à espera - adiciona-se ao fim para cumprir a disciplina FIFO LinkedListNode <object> myRequest = r.waiting.AddLast(new object()); do { try { //Espera por waitTimeout ms SyncUtils.Wait(map, myRequest, waitTimeout); } catch (ThreadInterruptedException) { //Se saiu por excepção então removo a thread da lista de espera r.waiting.Remove(myRequest); //A expecção é relançada throw; } //Se a thread saiu por timeOut mas há espaço para entrar então entra na mesma if (r.inside < maxInside) { //Removo a thread da lista de espera r.waiting.Remove(myRequest); //Thread entra na região r.inside++; //Conseguiu entrar logo retorna true return(true); } //enquanto não tiver passado timeOut. Isto porque podem ocorrer saidas espurias } while (SyncUtils.AdjustTimeout(ref lastTime, ref waitTimeout) != 0); //Se a thread saiu por timeOut e não houve espaço para entrar na região então removo da lista de espera r.waiting.Remove(myRequest); //Lanço timeOut exception throw new TimeoutException(); } //Não conseguiu entrar na região nem na lista de espera então retorna false return(false); } }