* When canonical input processing is enabled reads should only return

anything, if a line is available. Fixes bug #1483.
* Replaced the request owner waiting semaphore by a condition variable
  and removed the now no longer needed SemaphorePool.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22357 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-09-28 21:37:31 +00:00
parent ece77b379a
commit 2f6b4f3e1a
8 changed files with 88 additions and 246 deletions

View File

@ -8,6 +8,5 @@ KernelAddon <driver>tty :
line_buffer.cpp
master.cpp
slave.cpp
SemaphorePool.cpp
;
;

View File

@ -1,100 +0,0 @@
/*
* Copyright 2005-2007, Ingo Weinhold, bonefish@users.sf.net.
* Distributed under the terms of the MIT License.
*/
#include <new>
#include <util/AutoLock.h>
#include "SemaphorePool.h"
status_t
Semaphore::ZeroCount()
{
int32 count;
status_t error = get_sem_count(fSemaphore, &count);
if (error != B_OK)
return error;
if (count > 0)
error = acquire_sem_etc(fSemaphore, count, B_RELATIVE_TIMEOUT, 0);
else if (count < 0)
error = release_sem_etc(fSemaphore, -count, 0);
return error;
}
// #pragma mark -
SemaphorePool::SemaphorePool(int32 maxCached)
: fSemaphores(),
fCount(0),
fMaxCount(maxCached)
{
if (fMaxCount < 0)
fMaxCount = 8;
}
SemaphorePool::~SemaphorePool()
{
while (Semaphore *sem = fSemaphores.Head()) {
fSemaphores.Remove(sem);
delete sem;
}
mutex_destroy(&fLock);
}
status_t
SemaphorePool::Init()
{
return mutex_init(&fLock, "tty semaphore pool");
}
status_t
SemaphorePool::Get(Semaphore *&semaphore)
{
MutexLocker _(fLock);
// use a cached one, if available
if (fCount > 0) {
fCount--;
semaphore = fSemaphores.Head();
fSemaphores.Remove(semaphore);
return B_OK;
}
// create a new one
Semaphore *sem = new(nothrow) Semaphore(0, "pool semaphore");
if (!sem)
return B_NO_MEMORY;
semaphore = sem;
return B_OK;
}
void
SemaphorePool::Put(Semaphore *semaphore)
{
if (!semaphore)
return;
MutexLocker _(fLock);
if (fCount >= fMaxCount
|| semaphore->InitCheck() != B_OK
|| semaphore->ZeroCount() != B_OK) {
delete semaphore;
return;
}
fSemaphores.Add(semaphore);
fCount++;
}

View File

@ -1,67 +0,0 @@
/*
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
* Distributed under the terms of the MIT License.
*/
#ifndef TTY_SEMAPHORE_POOL_H
#define TTY_SEMAPHORE_POOL_H
#include <lock.h>
#include <util/DoublyLinkedList.h>
class Semaphore : public DoublyLinkedListLinkImpl<Semaphore> {
public:
Semaphore(sem_id sem)
: fSemaphore(sem)
{
}
Semaphore(int32 count, const char *name)
{
fSemaphore = create_sem(count, name);
}
~Semaphore()
{
if (fSemaphore >= 0)
delete_sem(fSemaphore);
}
status_t InitCheck() const
{
return (fSemaphore >= 0 ? B_OK : fSemaphore);
}
sem_id ID() const
{
return fSemaphore;
}
status_t ZeroCount();
private:
sem_id fSemaphore;
public:
Semaphore *fNext;
};
class SemaphorePool {
public:
SemaphorePool(int32 maxCached);
~SemaphorePool();
status_t Init();
status_t Get(Semaphore *&semaphore);
void Put(Semaphore *semaphore);
private:
typedef DoublyLinkedList<Semaphore> SemaphoreList;
struct mutex fLock;
SemaphoreList fSemaphores;
int32 fCount;
int32 fMaxCount;
};
#endif // TTY_SEMAPHORE_POOL_H

View File

@ -11,7 +11,6 @@
#include <lock.h>
#include "SemaphorePool.h"
#include "tty_private.h"
@ -34,8 +33,6 @@ static char *sDeviceNames[kNumTTYs * 2 + 1];
struct mutex gGlobalTTYLock;
struct mutex gTTYCookieLock;
struct recursive_lock gTTYRequestLock;
static char sSemaphorePoolBuffer[sizeof(SemaphorePool)];
SemaphorePool *gSemaphorePool = 0;
status_t
@ -73,15 +70,6 @@ init_driver(void)
return error;
}
// create the semaphore pool
gSemaphorePool
= new(sSemaphorePoolBuffer) SemaphorePool(kMaxCachedSemaphores);
error = gSemaphorePool->Init();
if (error != B_OK) {
uninit_driver();
return error;
}
// create driver name array and initialize basic TTY structures
char letter = 'p';
@ -124,11 +112,6 @@ uninit_driver(void)
for (int32 i = 0; i < (int32)kNumTTYs * 2; i++)
free(sDeviceNames[i]);
if (gSemaphorePool) {
gSemaphorePool->~SemaphorePool();
gSemaphorePool = NULL;
}
recursive_lock_destroy(&gTTYRequestLock);
mutex_destroy(&gTTYCookieLock);
mutex_destroy(&gGlobalTTYLock);

View File

@ -49,6 +49,24 @@ line_buffer_readable(struct line_buffer &buffer)
}
int32
line_buffer_readable_line(struct line_buffer &buffer, char eol, char eof)
{
size_t size = buffer.in;
if (size == 0)
return 0;
// find EOL or EOF char
for (size_t i = 0; i < size; i++) {
char c = buffer.buffer[(buffer.first + i) % buffer.size];
if (c == eol || c == '\n' || c == '\r' || c == eof)
return i + 1;
}
return 0;
}
int32
line_buffer_writable(struct line_buffer &buffer)
{

View File

@ -20,6 +20,7 @@ status_t init_line_buffer(struct line_buffer &buffer, size_t size);
status_t uninit_line_buffer(struct line_buffer &buffer);
status_t clear_line_buffer(struct line_buffer &buffer);
int32 line_buffer_readable(struct line_buffer &buffer);
int32 line_buffer_readable_line(struct line_buffer &buffer, char eol, char eof);
int32 line_buffer_writable(struct line_buffer &buffer);
ssize_t line_buffer_user_read(struct line_buffer &buffer, char *data,
size_t length, char eof = 0, bool* hitEOF = NULL);

View File

@ -19,7 +19,6 @@
#include <team.h>
#include "SemaphorePool.h"
#include "tty_private.h"
@ -134,7 +133,8 @@ class ReaderLocker : public AbstractLocker {
status_t AcquireReader(bool dontBlock);
private:
status_t _CheckBackgroundRead() const;
size_t _CheckAvailableBytes() const;
status_t _CheckBackgroundRead() const;
struct tty *fTTY;
RequestOwner fRequestOwner;
@ -295,7 +295,7 @@ RequestQueue::NotifyError(tty_cookie *cookie, status_t error)
RequestOwner::RequestOwner()
:
fSemaphore(NULL),
fConditionVariable(NULL),
fCookie(NULL),
fError(B_OK),
fBytesNeeded(1)
@ -364,20 +364,11 @@ RequestOwner::SetBytesNeeded(size_t bytesNeeded)
* The request lock MUST NOT be held!
*/
status_t
RequestOwner::Wait(bool interruptable, Semaphore *sem)
RequestOwner::Wait(bool interruptable)
{
TRACE(("%p->RequestOwner::Wait(%d, %p)\n", this, interruptable, sem));
TRACE(("%p->RequestOwner::Wait(%d)\n", this, interruptable));
// get a semaphore (if not supplied or invalid)
status_t error = B_OK;
bool ownsSem = false;
if (!sem || sem->InitCheck() != B_OK) {
error = gSemaphorePool->Get(sem);
if (error != B_OK)
return error;
ownsSem = true;
}
RecursiveLocker locker(gTTYRequestLock);
@ -386,35 +377,35 @@ RequestOwner::Wait(bool interruptable, Semaphore *sem)
&& (!fRequests[0].WasNotified() || !fRequests[1].WasNotified())) {
// not yet done
// set the semaphore
fSemaphore = sem;
// publish the condition variable
ConditionVariable<> conditionVariable;
conditionVariable.Publish(this, "tty request");
fConditionVariable = &conditionVariable;
// add an entry to wait on
ConditionVariableEntry<> entry;
entry.Add(this);
locker.Unlock();
// wait
TRACE(("%p->RequestOwner::Wait(): acquiring semaphore...\n", this));
TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
error = acquire_sem_etc(sem->ID(), 1,
(interruptable ? B_CAN_INTERRUPT : 0), 0);
error = entry.Wait(interruptable ? B_CAN_INTERRUPT : 0);
TRACE(("%p->RequestOwner::Wait(): semaphore acquired: %lx\n", this,
TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
error));
// remove the semaphore
locker.SetTo(gTTYRequestLock, false);
fSemaphore = NULL;
// remove the condition variable
locker.Lock();
fConditionVariable = NULL;
conditionVariable.Unpublish();
}
// get the result
if (error == B_OK)
error = fError;
locker.Unlock();
// return the semaphore to the pool
if (ownsSem)
gSemaphorePool->Put(sem);
return error;
}
@ -439,18 +430,18 @@ RequestOwner::Notify(Request *request)
TRACE(("%p->RequestOwner::Notify(%p)\n", this, request));
if (fError == B_OK && !request->WasNotified()) {
bool releaseSem = false;
bool notify = false;
if (&fRequests[0] == request) {
releaseSem = fRequests[1].WasNotified();
notify = fRequests[1].WasNotified();
} else if (&fRequests[1] == request) {
releaseSem = fRequests[0].WasNotified();
notify = fRequests[0].WasNotified();
} else {
// spurious call
}
if (releaseSem && fSemaphore)
release_sem(fSemaphore->ID());
if (notify && fConditionVariable)
fConditionVariable->NotifyOne();
}
}
@ -464,8 +455,8 @@ RequestOwner::NotifyError(Request *request, status_t error)
fError = error;
if (!fRequests[0].WasNotified() || !fRequests[1].WasNotified()) {
if (fSemaphore)
release_sem(fSemaphore->ID());
if (fConditionVariable)
fConditionVariable->NotifyOne();
}
}
}
@ -667,7 +658,7 @@ ReaderLocker::AcquireReader(bool dontBlock)
// check, if we're first in queue, and if there is something to read
if (fRequestOwner.IsFirstInQueues()) {
fBytes = line_buffer_readable(fTTY->input_buffer);
fBytes = _CheckAvailableBytes();
if (fBytes > 0)
return B_OK;
}
@ -686,12 +677,27 @@ ReaderLocker::AcquireReader(bool dontBlock)
status = _CheckBackgroundRead();
if (status == B_OK)
fBytes = line_buffer_readable(fTTY->input_buffer);
fBytes = _CheckAvailableBytes();
return status;
}
size_t
ReaderLocker::_CheckAvailableBytes() const
{
// Reading from the slave with canonical input processing enabled means
// that we read at max until hitting a line end or EOF.
if (!fTTY->is_master && (fTTY->settings->termios.c_lflag & ICANON) != 0) {
return line_buffer_readable_line(fTTY->input_buffer,
fTTY->settings->termios.c_cc[VEOL],
fTTY->settings->termios.c_cc[VEOF]);
}
return line_buffer_readable(fTTY->input_buffer);
}
status_t
ReaderLocker::_CheckBackgroundRead() const
{
@ -1028,11 +1034,8 @@ tty_close_cookie(struct tty_cookie *cookie)
ttyLocker.Unlock();
// wait for our turn (we reuse the blocking semaphore, so Wait()
// can't fail due to not being able to get a semaphore)
Semaphore sem(cookie->blocking_semaphore);
cookie->blocking_semaphore = -1;
requestOwner.Wait(false, &sem);
// wait for our turn
requestOwner.Wait(false);
// re-lock
ttyLocker.SetTo(cookie->tty->lock, false);
@ -1402,11 +1405,17 @@ tty_input_read(tty_cookie *cookie, void *buffer, size_t *_length)
return status;
}
size_t toRead = locker.AvailableBytes();
if (toRead == 0)
continue;
if (toRead > length)
toRead = length;
bool _hitEOF = false;
bool* hitEOF = (tty->pending_eof > 0 ? &_hitEOF : NULL);
bytesRead = line_buffer_user_read(tty->input_buffer, (char *)buffer,
length, tty->settings->termios.c_cc[VEOF], hitEOF);
toRead, tty->settings->termios.c_cc[VEOF], hitEOF);
if (bytesRead < B_OK) {
*_length = 0;
return bytesRead;

View File

@ -5,15 +5,16 @@
#ifndef TTY_PRIVATE_H
#define TTY_PRIVATE_H
#include <KernelExport.h>
#include <Drivers.h>
#include <lock.h>
#include <fs/select_sync_pool.h>
#include <util/DoublyLinkedList.h>
#include <termios.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <condition_variable.h>
#include <fs/select_sync_pool.h>
#include <lock.h>
#include <util/DoublyLinkedList.h>
#include "line_buffer.h"
@ -29,7 +30,6 @@ typedef status_t (*tty_service_func)(struct tty *tty, uint32 op);
class RequestOwner;
class Semaphore;
class SemaphorePool;
struct tty;
struct tty_cookie;
@ -87,7 +87,7 @@ class RequestOwner {
void SetBytesNeeded(size_t bytesNeeded);
size_t BytesNeeded() const { return fBytesNeeded; }
status_t Wait(bool interruptable, Semaphore *sem = NULL);
status_t Wait(bool interruptable);
bool IsFirstInQueues();
@ -97,12 +97,12 @@ class RequestOwner {
status_t Error() const { return fError; }
private:
Semaphore *fSemaphore;
tty_cookie *fCookie;
status_t fError;
RequestQueue *fRequestQueues[2];
Request fRequests[2];
size_t fBytesNeeded;
ConditionVariable<>* fConditionVariable;
tty_cookie* fCookie;
status_t fError;
RequestQueue* fRequestQueues[2];
Request fRequests[2];
size_t fBytesNeeded;
};
@ -151,7 +151,6 @@ extern device_hooks gSlaveTTYHooks;
extern struct mutex gGlobalTTYLock;
extern struct mutex gTTYCookieLock;
extern struct recursive_lock gTTYRequestLock;
extern SemaphorePool *gSemaphorePool;
// functions available for master/slave TTYs