The Pedigree Project  0.1
Pipe.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "Pipe.h"
21 #include "pedigree/kernel/LockGuard.h"
22 #include "pedigree/kernel/process/Mutex.h"
23 #include "pedigree/kernel/process/Process.h"
24 #include "pedigree/kernel/process/Thread.h"
25 #include "pedigree/kernel/processor/Processor.h"
26 #include "pedigree/kernel/processor/ProcessorInformation.h"
27 #include "pedigree/kernel/utilities/ZombieQueue.h"
28 #include "pedigree/kernel/utilities/new"
29 
30 class Filesystem;
31 
32 class ZombiePipe : public ZombieObject
33 {
34  public:
35  ZombiePipe(Pipe *pPipe) : m_pPipe(pPipe)
36  {
37  }
38  virtual ~ZombiePipe();
39 
40  private:
41  Pipe *m_pPipe;
42 };
43 
44 ZombiePipe::~ZombiePipe()
45 {
46  NOTICE("ZombiePipe: freeing " << m_pPipe);
47  delete m_pPipe;
48 }
49 
51  : File(), m_bIsAnonymous(true), m_bIsEOF(false), m_Buffer(PIPE_BUF_MAX),
52  m_ReaderSem(0)
53 {
54 #ifdef VERBOSE_KERNEL
55  NOTICE("Pipe: new anonymous pipe " << reinterpret_cast<uintptr_t>(this));
56 #endif
57 }
58 
60  const String &name, Time::Timestamp accessedTime,
61  Time::Timestamp modifiedTime, Time::Timestamp creationTime, uintptr_t inode,
62  Filesystem *pFs, size_t size, File *pParent, bool bIsAnonymous)
63  : File(
64  name, accessedTime, modifiedTime, creationTime, inode, pFs, size,
65  pParent),
66  m_bIsAnonymous(bIsAnonymous), m_bIsEOF(false), m_Buffer(PIPE_BUF_MAX),
67  m_ReaderSem(0)
68 {
69 #ifdef VERBOSE_KERNEL
70  NOTICE(
71  "Pipe: new " << (bIsAnonymous ? "anonymous" : "named") << " pipe "
72  << Hex << this);
73 #endif
74 }
75 
77 {
78  // ensure anything else in the critical section can finish before we clean
79  // up fully
80  // this is useful for cases where ZombieQueue destroys us before we get a
81  // chance to actually return from decreaseRefCount (which accesses the lock)
82  m_Lock.acquire();
83  m_Lock.release();
84 }
85 
86 int Pipe::select(bool bWriting, int timeout)
87 {
88  if (bWriting)
89  {
90  return m_Buffer.canWrite(timeout > 0) ? 1 : 0;
91  }
92  else
93  {
94  return m_Buffer.canRead(timeout > 0) ? 1 : 0;
95  }
96 }
97 
99  uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
100 {
101  // Need to read what's left in the pipe then EOF if there's no more readers!
102  if (m_nWriters == 0)
103  {
104  bCanBlock = false;
105  }
106 
107  uint8_t *pBuf = reinterpret_cast<uint8_t *>(buffer);
108  return m_Buffer.read(pBuf, size, bCanBlock);
109 }
110 
112  uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock)
113 {
114  if (m_nReaders == 0)
115  {
116  // no more readers, abort the write
117  return 0;
118  }
119 
120  uint8_t *pBuf = reinterpret_cast<uint8_t *>(buffer);
121  uint64_t result = m_Buffer.write(pBuf, size, bCanBlock);
122  if (result)
123  {
124  dataChanged();
125  }
126 
127  return result;
128 }
129 
130 bool Pipe::isPipe() const
131 {
132  return getName().length() == 0 || m_bIsAnonymous;
133 }
134 
135 bool Pipe::isFifo() const
136 {
137  return getName().length() > 0 && !m_bIsAnonymous;
138 }
139 
140 void Pipe::increaseRefCount(bool bIsWriter)
141 {
142  if (bIsWriter)
143  {
144  // Enable writes if they were previously disabled.
145  if (!m_Buffer.enableWrites())
146  {
147  // Writes were disabled previously (EOF), so wipe the pipe.
148  m_Buffer.wipe();
149  }
150  m_nWriters++;
151  }
152  else
153  {
154  // A reader is now present so we can enable reads if they weren't.
156  m_nReaders++;
157 
159  }
160 }
161 
162 void Pipe::decreaseRefCount(bool bIsWriter)
163 {
164  // Make sure only one thread decreases the refcount at a time. This is
165  // important as we add ourselves to the ZombieQueue if the refcount ticks
166  // to zero. Getting pre-empted by another thread that also decreases the
167  // refcount between the decrement and the check for zero may mean the pipe
168  // is added to the ZombieQueue twice, which causes a double free.
169  bool bDataChanged = false;
170  {
171  LockGuard<Mutex> guard(m_Lock);
172 
173  if (m_nReaders == 0 && m_nWriters == 0)
174  {
175  // Refcount is already zero - don't decrement! (also, bad.)
176  ERROR("Pipe: decreasing refcount when refcount is already zero.");
177  return;
178  }
179 
180  if (bIsWriter)
181  {
182  m_nWriters--;
183  if (m_nWriters == 0)
184  {
185  // Wakes up readers waiting as they won't be able to be woken
186  // by new bytes being written anymore.
188  bDataChanged = true;
189  }
190  }
191  else
192  {
193  m_nReaders--;
194  if (m_nReaders == 0)
195  {
196  // Wake up any writers that were waiting for space - no more
197  // readers (EOF condition, pipe other end has left).
199  bDataChanged = true;
200  }
201  }
202 
203  if (m_nReaders == 0 && m_nWriters == 0)
204  {
205  // If we're anonymous, die completely.
206  if (m_bIsAnonymous)
207  {
208  size_t pid = Processor::information()
209  .getCurrentThread()
210  ->getParent()
211  ->getId();
212 #ifdef VERBOSE_KERNEL
213  NOTICE(
214  "Adding pipe [" << pid << "] " << this
215  << " to ZombieQueue");
216 #endif
217  ZombieQueue::instance().addObject(new ZombiePipe(this));
218  bDataChanged = false;
219  }
220  }
221  }
222 
223  if (bDataChanged)
224  {
225  dataChanged();
226  }
227 }
228 
229 bool Pipe::waitForReader()
230 {
231  return m_ReaderSem.acquire();
232 }
void wipe()
Definition: Buffer.cc:466
virtual bool isFifo() const
Definition: Pipe.cc:135
volatile bool m_bIsEOF
Definition: Pipe.h:110
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition: Semaphore.h:62
Semaphore m_ReaderSem
Definition: Pipe.h:116
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition: Pipe.cc:111
bool canRead(bool block)
Definition: Buffer.cc:437
bool canWrite(bool block)
Definition: Buffer.cc:409
virtual bool isPipe() const
Definition: Pipe.cc:130
Buffer< uint8_t > m_Buffer
Definition: Pipe.h:113
size_t read(T *buffer, size_t count, bool block=true)
Definition: Buffer.cc:236
Definition: String.h:49
static ProcessorInformation & information()
Definition: Processor.cc:45
bool enableWrites()
Definition: Buffer.cc:378
void disableReads()
Definition: Buffer.cc:368
void release(size_t n=1)
Definition: Semaphore.cc:239
#define NOTICE(text)
Definition: Log.h:74
virtual int select(bool bWriting=false, int timeout=0)
Definition: Pipe.cc:86
Definition: Log.h:136
virtual ~Pipe()
Definition: Pipe.cc:76
void dataChanged()
Definition: File.cc:654
bool m_bIsAnonymous
Definition: Pipe.h:107
Pipe()
Definition: Pipe.cc:50
String getName() const
Definition: File.cc:411
virtual void decreaseRefCount(bool bIsWriter)
Definition: Pipe.cc:162
#define ERROR(text)
Definition: Log.h:82
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
Definition: Pipe.cc:98
size_t write(const T *buffer, size_t count, bool block=true)
Definition: Buffer.cc:64
Definition: File.h:66
void disableWrites()
Definition: Buffer.cc:358
bool enableReads()
Definition: Buffer.cc:387
Definition: Pipe.h:35