]> git.tdb.fi Git - libs/core.git/blob - source/pipe.cpp
696abe5c5695c89d45c048ec2af2946b68a23c1b
[libs/core.git] / source / pipe.cpp
1 /* $Id$
2
3 This file is part of libmspio
4 Copyright © 2007 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #ifndef WIN32
9 #include <fcntl.h>
10 #include <errno.h>
11 #endif
12 #include <msp/strings/formatter.h>
13 #include "pipe.h"
14
15 using namespace std;
16
17 namespace Msp {
18 namespace IO {
19
20 Pipe::Pipe()
21 {
22 #ifdef WIN32
23         string name=format("\\\\.\\pipe\\%u.%p", GetCurrentProcessId(), this);
24         handle[0]=CreateNamedPipe(name.c_str(), PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 1, 1024, 1024, 0, 0);
25         if(handle[0]==INVALID_HANDLE_VALUE)
26                 throw SystemError("Unable to create pipe", GetLastError());
27
28         handle[1]=CreateFile(name.c_str(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
29         if(handle[1]==INVALID_HANDLE_VALUE)
30         {
31                 unsigned err=GetLastError();
32                 CloseHandle(handle[0]);
33                 throw SystemError("Unable to create pipe", err);
34         }
35
36         overlapped=0;
37         event=CreateEvent(0, true, false, 0);
38         buf_size=1024;
39         buffer=new char[buf_size];
40         buf_avail=0;
41         buf_next=buffer;
42 #else
43         if(pipe(handle)==-1)
44                 throw SystemError("Unable to create pipe", errno);
45 #endif
46
47         set_events(P_INPUT);
48 }
49
50 void Pipe::set_block(bool b)
51 {
52         mode=(mode&~M_NONBLOCK);
53         if(b)
54                 mode=(mode|M_NONBLOCK);
55
56 #ifndef WIN32
57         int flags=fcntl(handle[0], F_GETFD);
58         fcntl(handle[0], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
59         flags=fcntl(handle[1], F_GETFD);
60         fcntl(handle[1], F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
61 #endif
62 }
63
64 void Pipe::close()
65 {
66         set_events(P_NONE);
67
68         signal_flush_required.emit();
69 #ifdef WIN32
70         CloseHandle(handle[0]);
71         CloseHandle(handle[1]);
72 #else
73         ::close(handle[0]);
74         ::close(handle[1]);
75         signal_closed.emit();
76 #endif
77 }
78
79 Handle Pipe::get_event_handle()
80 {
81 #ifdef WIN32
82         if(!overlapped && !buf_avail)
83         {
84                 overlapped=new OVERLAPPED;
85                 memset(overlapped, 0, sizeof(OVERLAPPED));
86                 overlapped->hEvent=event;
87
88                 DWORD ret;
89                 buf_next=buffer;
90                 if(!ReadFile(handle[0], buffer, buf_size, &ret, overlapped))
91                 {
92                         unsigned err=GetLastError();
93                         if(err!=ERROR_IO_PENDING)
94                                 throw SystemError("Failed to start an overlapped read", err);
95                 }
96                 else
97                 {
98                         buf_avail=ret;
99                         delete overlapped;
100                         overlapped=0;
101                         SetEvent(event);
102                 }
103         }
104
105         return event;
106 #else
107         return handle[0];
108 #endif
109 }
110
111 Pipe::~Pipe()
112 {
113         close();
114 }
115
116 unsigned Pipe::do_write(const char *buf, unsigned size)
117 {
118         if(size==0)
119                 return 0;
120
121 #ifdef WIN32
122         DWORD ret;
123         if(!WriteFile(handle[1], buf, size, &ret, 0))
124                 throw SystemError("Writing to pipe failed", GetLastError());
125 #else
126         int ret=::write(handle[1], buf, size);
127         if(ret==-1)
128         {
129                 if(errno==EAGAIN)
130                         return 0;
131                 else
132                         throw SystemError("Writing to pipe failed", errno);
133         }
134 #endif
135
136         return ret;
137 }
138
139 unsigned Pipe::do_read(char *buf, unsigned size)
140 {
141         if(size==0)
142                 return 0;
143
144 #ifdef WIN32
145         // Initiate overlapped read if needed
146         get_event_handle();
147
148         if(overlapped)
149         {
150                 DWORD ret;
151                 if(!GetOverlappedResult(handle[0], overlapped, &ret, !buf_avail))
152                         throw SystemError("Reading from pipe failed", GetLastError());
153                 else
154                 {
155                         buf_avail+=ret;
156                         delete overlapped;
157                         overlapped=0;
158                 }
159         }
160
161         unsigned ret=min(buf_avail, size);
162         memcpy(buf, buf_next, ret);
163         buf_next+=ret;
164         buf_avail-=ret;
165
166         // Initiate another overlapped read in case someone is polling us
167         get_event_handle();
168 #else
169         int ret=::read(handle[0], buf, size);
170         if(ret==-1)
171         {
172                 if(errno==EAGAIN)
173                         return 0;
174                 else
175                         throw SystemError("Reading from pipe failed", errno);
176         }
177 #endif
178
179         if(ret==0)
180         {
181                 eof_flag=true;
182                 signal_end_of_file.emit();
183         }
184
185         return ret;
186 }
187
188 } // namespace IO
189 } // namespace Msp