Code::Blocks  SVN r11506
ipc.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3  * http://www.gnu.org/licenses/gpl-3.0.html
4  *
5  * $Revision: 8245 $
6  * $Id: ipc.cpp 8245 2012-08-22 06:51:14Z mortenmacfly $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/src/src/ipc.cpp $
8  */
9 
10 #include "ipc.h"
11 #include "main.h"
12 #include <wx/tokenzr.h>
13 
14 const wxString g_failed_shm(_T("Failed creating shared memory initialising IPC (error 0x00000d04)."));
15 const wxString g_failed_sem(_T("Failed creating semaphore/mutex initialising IPC (error 0x007f0002)."));
16 
17 void IPC::Send(const wxString& in)
18 {
19  if (in.length() * sizeof(wxChar) > shm.Size())
20  cbThrow(_T("Input exceeds shared memory size (error 0x0000cde0)."));
21 
22  if (shm.Lock(SharedMemory::writer) == 0)
23  {
24  // If locking failed here, this means the semaphore (and hence the shared memory, and the server process) was destroyed
25  // after we *just* checked that it exists (a few nanoseconds ago). This is a funny race condition
26  // which should be really, really rare, but which is of course nevertheless possible.
27  // We should consequently turn this process into a server, after seeing that the semaphore died, but this is really awful,
28  // so... we're not doing that... for now. The worst thing to happen is that double-clicking a file does not do anything once in a million times.
29  //
30  // Let's just throw and see how often we see this exception in normal everyday use.
31  // If it never happens, then simply ignoring the issue is a perfectly acceptable solution.
32  //
33  cbThrow(_T("Congrats, you managed to kill process 1 within nanoseconds after launching process 2, which is quite hard to do.\n\nPlease inform the Code::Blocks team of your achievement."));
34  }
35 
36  memcpy(shm.BasePointer(), in.c_str(), (in.length()+1) * sizeof(wxChar));
38 }
39 
40 
42 {
43  // Other than POSIX, Windows does not signal threads waiting for a semaphore when the semaphore is deleted (at least, MSDN says so),
44  // therefore we have to do unlock by hand before deleting, or we may lock up a process for all times.
45  // IMPORTANT: This must be called from Manager::Shutdown() or from any other appropriate place
46  is_shutdown = true;
48 };
49 
50 
51 wxThread::ExitCode IPC::Entry() /* this is the receiving end */
52 {
53  for(;;)
54  {
56  return 0;
57 
58  MainFrame* cbframe = static_cast<MainFrame*>(Manager::Get()->GetAppFrame());
59  if (cbframe == nullptr)
60  return 0;
61 
62  cbframe->OnDropFiles(0,0, wxStringTokenize((const wxChar*) shm.BasePointer(), _T("\n"), wxTOKEN_STRTOK));
63 
65 
66  if (is_shutdown)
67  return 0;
68  }
69 }
70 
71 
72 #if defined (__WIN32__) /* ------------------------------------------------------------- */
73 
74 
75 SharedMemory::SharedMemory() : handle(0), semid(0), shared(0), ok(false), server(false)
76 {
77  SetLastError(0); // CreateSemaphore should already do this, but anyway...
78  sem[reader] = CreateSemaphore(nullptr, 0, 1, TEXT("CdeBlsemIPCr"));
79  sem[writer] = CreateSemaphore(nullptr, 1, 1, TEXT("CdeBlsemIPCw"));
80 
81  if (GetLastError() != ERROR_ALREADY_EXISTS)
82  {
83  server = true;
84  }
85 
86  handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, ipc_buf_size, TEXT("CdeBlshmIPC"));
87 
88  if (handle == 0 || (shared = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, ipc_buf_size)) == 0)
89  {
91  return;
92  }
93 
94  ok = true;
95 }
96 
98 {
99  UnmapViewOfFile(shared);
100  CloseHandle(handle);
101  CloseHandle(sem[reader]);
102  CloseHandle(sem[writer]);
103 }
104 
106 {
107  if (rw == reader)
108  {
109  return WaitForSingleObject(sem[reader], INFINITE) == WAIT_OBJECT_0
110  && WaitForSingleObject(sem[writer], INFINITE) == WAIT_OBJECT_0;
111  }
112  else // if (rw == writer)
113  {
114  return WaitForSingleObject(sem[writer], INFINITE) == WAIT_OBJECT_0;
115  }
116 
117  return false;
118 }
119 
121 {
122  if (rw == reader)
123  {
124  ReleaseSemaphore(sem[writer], 1, nullptr);
125  }
126  else // if (rw == writer)
127  {
128  ReleaseSemaphore(sem[reader], 1, nullptr);
129  ReleaseSemaphore(sem[writer], 1, nullptr);
130  Sleep(0);
131  }
132 }
133 
134 
135 #else /* ------------------------------------------------------------- */
136 
137 
138 SharedMemory::SharedMemory() : handle(0), semid(0), shared(0), ok(false), server(false)
139 {
140  char file[256];
141  key_t key;
142 
143  /* Shared memory and semaphore functions expect unique IDs created with ftok().
144  * Unluckily, this is how POSIX works... we need a unique yet reproducable filename,
145  * and the file must exist, too.
146  * We'll use the executable file for this, if we can figure it out via /proc.
147  * Unluckily, again, this is nowhere near standardised or even guaranteed, so
148  * we'll have to create a file in /tmp if everything fails...
149  */
150  if (readlink("/proc/self/exe", file, sizeof(file)) < 0) /* Linux style */
151  {
152  if (readlink("/proc/self/file", file, sizeof(file)) < 0) /* failed, try BSD style */
153  {
154  strcpy(file, "/tmp/fuckyou"); /* failed again, use some bullshit */
155  close(open(file, O_CREAT, O_RDONLY|O_WRONLY));
156  }
157  }
158 
159  key = ftok(file, 'a');
160  semid = semget(key, 2, IPC_CREAT | 0666);
161 
162  if (semid == -1)
163  {
165  return;
166  }
167 
168  key = ftok(file, 'b');
169  handle = shmget(key, ipc_buf_size, 0666 | IPC_CREAT | IPC_EXCL);
170 
171  if (handle == -1) /* failed, because... */
172  {
173  if (errno == EEXIST) /* EEXIST ---> server already running */
174  {
175  handle = shmget(key, ipc_buf_size, 0666);
176  if (handle == -1)
177  {
179  return;
180  }
181  ok = true;
182  server = false;
183  }
184  else /* ...any other error ---> bad */
185  {
187  return;
188  }
189  }
190  else
191  {
192  ok = true;
193  server = true;
194 
195  unsigned short int v[2] = {0, 1};
196  semctl(semid, 0, SETALL, v);
197  }
198 
199  shared = shmat(handle, nullptr, 0);
200  ok = (shared != (void*) -1) ? ok : false;
201 }
202 
203 
205 {
206  shmdt(shared);
207  if (server)
208  {
209  shmctl(handle, IPC_RMID, 0);
210  semctl(semid, 0, IPC_RMID ); /* this will wake up the thread blocking in semop() */
211  }
212 }
213 
214 bool SharedMemory::Lock(rw_t rw)
215 {
216  if (rw == reader)
217  {
218  sembuf op[2];
219 
220  op[0].sem_num = reader;
221  op[0].sem_op = -1;
222  op[0].sem_flg = 0;
223 
224  op[1].sem_num = writer;
225  op[1].sem_op = -1;
226  op[1].sem_flg = 0;
227 
228  return semop(semid, op, 2) == 0; /* if semaphore is deleted, EIDRM or EINVAL will be returned */
229  }
230 
231  if (rw == writer)
232  {
233  sembuf op[1];
234  op[0].sem_num = writer;
235  op[0].sem_op = -1;
236  op[0].sem_flg = 0;
237 
238  return semop(semid, op, 1) == 0;
239  }
240 
241  return false;
242 }
243 
244 void SharedMemory::Unlock(rw_t rw)
245 {
246  if (rw == writer)
247  {
248  sembuf op[2];
249  op[0].sem_num = reader;
250  op[0].sem_op = 1;
251  op[0].sem_flg = 0;
252 
253  op[1].sem_num = writer;
254  op[1].sem_op = 1;
255  op[1].sem_flg = 0;
256 
257  semop(semid, op, 2);
258  }
259 
260  if (rw == reader)
261  {
262  sembuf op[1];
263  op[0].sem_num = writer;
264  op[0].sem_op = 1;
265  op[0].sem_flg = 0;
266 
267  semop(semid, op, 1);
268  }
269 }
270 
271 #endif /* ------------------------------------------------------------- */
Definition: main.h:60
const wxString g_failed_sem(_T("Failed creating semaphore/mutex initialising IPC (error 0x007f0002)."))
virtual ExitCode Entry()
Definition: ipc.cpp:51
static Manager * Get()
Use Manager::Get() to get a pointer to its instance Manager::Get() is guaranteed to never return an i...
Definition: manager.cpp:182
volatile bool is_shutdown
Definition: ipc.h:97
size_t length() const
wxCStrData c_str() const
bool Lock(rw_t rw)
Definition: ipc.cpp:105
static LogManager * Get()
Definition: manager.h:199
#define _T(string)
bool ok
Definition: ipc.h:56
static const int ipc_buf_size
Definition: ipc.h:42
size_t Size() const
Definition: ipc.h:69
~SharedMemory()
Definition: ipc.cpp:97
void Panic(const wxString &msg, const wxString &component=wxEmptyString)
Definition: logmanager.cpp:197
wxUSE_UNICODE_dependent wxChar
shm_handle_t handle
Definition: ipc.h:47
const wxString g_failed_shm(_T("Failed creating shared memory initialising IPC (error 0x00000d04)."))
void Shutdown()
Definition: ipc.cpp:41
SharedMemory shm
Definition: ipc.h:98
wxFrame * GetAppFrame() const
Definition: manager.cpp:419
SharedMemory()
Definition: ipc.cpp:75
void * BasePointer() const
Definition: ipc.h:68
#define cbThrow(message)
Definition: cbexception.h:42
void Unlock(rw_t rw)
Definition: ipc.cpp:120
void * shared
Definition: ipc.h:55
bool server
Definition: ipc.h:57
wxArrayString wxStringTokenize(const wxString &str, const wxString &delims=wxDEFAULT_DELIMITERS, wxStringTokenizerMode mode=wxTOKEN_DEFAULT)
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames)
Definition: main.cpp:2396
void Send(const wxString &value)
Definition: ipc.cpp:17
void * ExitCode