Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Sauermann J.Realtime operating systems.Concepts and implementation of microkernels for embedded systems.1997.pdf
Скачиваний:
29
Добавлен:
23.08.2013
Размер:
1.32 Mб
Скачать

3. Kernel Implementation

51

 

 

3.5Queues

As we already saw, there are different kinds of queues, depending on where semaphores are used. But common to all queues is a ring buffer. Hence we implement ring buffers as a separate class from which the different queues are derived. Since a ring buffer may contain any kind of items, we make a template class called RingBuffer.

1 // Queue.hh

...

12template <class Type> class RingBuffer

13{

14public:

15RingBuffer(unsigned int Size);

16~RingBuffer();

17

 

 

 

 

18

int IsEmpty() const {

return (count)

? 0

: -1; };

19

int IsFull() const {

return (count < size) ? 0

: -1; };

20

 

 

 

 

21

int Peek(Type & dest)

const;

 

 

22

 

 

 

 

23protected:

24enum { QUEUE_OK = 0, QUEUE_FAIL = -1 };

26virtual int PolledGet(Type & dest) = 0;

27virtual int PolledPut(const Type & dest) = 0;

28inline void GetItem(Type & source);

29inline void PutItem(const Type & src);

31unsigned int size;

32unsigned int count;

34private:

35

Type *

data;

36unsigned int get;

37unsigned int put;

38};

3.5.1 Ring Buffer Constructor and Destructor

The constructor initializes the put and get indices to 0, the count of items in the buffer to 0, and stores the size of the buffer. Then the constructor allocates a buffer big enough to store size instances of class Type.

1 // Queue.cc

...

9template <class Type> RingBuffer<Type>::RingBuffer(unsigned int Size)

10: size(Size), get(0), put(0), count(0)

11

12{

13data = new Type[size];

14:

The destructor releases the memory allocated for the buffer.

1 // Queue.cc

52

3.5 Queues

 

 

...

16template <class Type> RingBuffer<Type>::~RingBuffer()

17{

18delete [] data;

19}

3.5.2 RingBuffer Member Functions

The member functions IsEmpty() and IsFull() are self-explanatory. Peek(Type & dest) returns QUEUE_FAIL (i.e. nonzero) if the queue is empty. Otherwise, it stores the next item in the queue in dest, but without removing it from the queue. The Peek() function is useful for scanners which usually require a single character look-ahead. Traditionally, a character looked ahead is pushed back into a queue by means of a function unput(char) if the character is not required. But this solution causes several problems. ??? Which problems ??? So providing a look-ahead function like Peek() is the better solution, as it does not remove any item from the queue.

1 // Queue.cc

...

21template <class Type> int RingBuffer<Type>::Peek(Type & dest) const

22{

23int ret = QUEUE_FAIL;

24

25{

26os::INT_MASK old_INT_MASK = os::set_INT_MASK(os::NO_INTS);

27

if (count) { dest = data[get];

ret = QUEUE_OK; }

28os::set_INT_MASK(old_INT_MASK);

29}

30return ret;

31}

The member function PutItem() inserts, and GetItem() removes an item from the queue. However, PutItem() assumes that the queue is not full when it is called, and GetItem() assumes that the queue is not empty. This condition is not checked, because the check as such is different for queues that use semaphores and queues that do not use semaphores. Apart from that, interrupts are in general to be disabled when these functions are called. To avoid direct usage of these functions, they are made protected so that only classes derived from RingBuffer can use them.

33template <class Type> inline void RingBuffer<Type>::GetItem(Type & dest)

34{

35dest = data[get++];

36

if (get >= size) get = 0;

37count--;

38}

...

3. Kernel Implementation

53

 

 

40template <class Type> inline void RingBuffer<Type>::PutItem(const Type &src)

41{

42data[put++] = src;

43

if (put >= size) put = 0;

44count++;

45}

Finally, it has shown to be useful to provide polled access to both ends of a queue, even if semaphores are used. For this purpose, the member functions PolledGet() and PolledPut() are used. Their implementation depends on where semaphores are used; thus they are purely virtual.

3.5.3 Queue Put and Get Functions

The polled and semaphore-controlled Put() and Get() for the four possible types of queues result in a total of 12 functions. Rather than explaining them all in detail, we only present the basic principles:

Interrupts are disabled while the ring buffer is accessed.

For polled operation, if a semaphore is used at the polled end of the queue, the semaphore is polled as well in order to keep the semaphore synchronized with the item count.

It is always checked if the queue is full before PutItem is called, and if the queue is empty before GetItem is called. This check is explicit if no semaphore is used at the respective ends, or implicit by polling the semaphore.

3.5.4 Queue Put and Get Without Disabling Interrupts

In the implementation shown, the manipulation of the queue is always performed with interrupts enabled. Considering the short code, this causes a significant overhead. Often interrupts are already disabled anyway, e.g. in interrupt service routines. In those cases, one can derive other queue classes from RingBuffer that do not disable interrupts.

It should also be noted that the get and put ends of the queue are more or less independent of each other. As we have seen in PutItem() and GetItem(), the count is always modified after putting or getting an item. If incrementing or decreasing count is atomic (which is the case for most compilers), and if there is only one task or interrupt service routine at all (which is the case for most queues), then it is not necessary at all to disable interrupts. It may as well be the case that interrupts need to be disabled only at one end of a queue, e.g. for one task that receives messages from several other tasks. A good candidate for such optimizations are the character input and output queues for serial I/O.