blob: 5234024a2730a58fd0634806edc41646e0a22737 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- Communication.cpp ---------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10// C Includes
11// C++ Includes
12// Other libraries and framework includes
13// Project includes
14#include "lldb/lldb-private-log.h"
15#include "lldb/Core/Communication.h"
16#include "lldb/Core/Connection.h"
17#include "lldb/Core/Log.h"
18#include "lldb/Core/Timer.h"
19#include "lldb/Core/Event.h"
Eli Friedman88966972010-06-09 08:50:27 +000020#include <string.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000021
22using namespace lldb;
23using namespace lldb_private;
24
25//----------------------------------------------------------------------
26// Constructor
27//----------------------------------------------------------------------
28Communication::Communication(const char *name) :
29 Broadcaster (name),
30 m_connection_ap (),
Eli Friedman8878f872010-07-09 22:53:18 +000031 m_read_thread (LLDB_INVALID_HOST_THREAD),
Chris Lattner30fdc8d2010-06-08 16:52:24 +000032 m_read_thread_enabled (false),
33 m_bytes(),
34 m_bytes_mutex (Mutex::eMutexTypeRecursive),
35 m_callback (NULL),
36 m_callback_baton (NULL)
37
38{
39 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION,
40 "%p Communication::Communication (name = %s)",
41 this, name);
42}
43
44//----------------------------------------------------------------------
45// Destructor
46//----------------------------------------------------------------------
47Communication::~Communication()
48{
49 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION,
50 "%p Communication::~Communication (name = %s)",
51 this, m_broadcaster_name.AsCString(""));
52 Clear();
53}
54
55void
56Communication::Clear()
57{
58 StopReadThread (NULL);
59 Disconnect (NULL);
60}
61
62ConnectionStatus
63Communication::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
64{
65 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::BytesAvailable (timeout_usec = %u)", this, timeout_usec);
66
67 if (m_connection_ap.get())
68 return m_connection_ap->BytesAvailable (timeout_usec, error_ptr);
69 if (error_ptr)
70 error_ptr->SetErrorString("Invalid connection.");
71 return eConnectionStatusNoConnection;
72}
73
74ConnectionStatus
75Communication::Connect (const char *url, Error *error_ptr)
76{
77 Clear();
78
79 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Connect (url = %s)", this, url);
80
81 if (m_connection_ap.get())
82 return m_connection_ap->Connect (url, error_ptr);
83 if (error_ptr)
84 error_ptr->SetErrorString("Invalid connection.");
85 return eConnectionStatusNoConnection;
86}
87
88ConnectionStatus
89Communication::Disconnect (Error *error_ptr)
90{
91 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Disconnect ()", this);
92
93 if (m_connection_ap.get())
94 {
95 ConnectionStatus status = m_connection_ap->Disconnect (error_ptr);
96 m_connection_ap.reset();
97 return status;
98 }
99 return eConnectionStatusNoConnection;
100}
101
102bool
103Communication::IsConnected () const
104{
105 if (m_connection_ap.get())
106 return m_connection_ap->IsConnected ();
107 return false;
108}
109
110bool
111Communication::HasConnection () const
112{
113 return m_connection_ap.get() != NULL;
114}
115
116size_t
117Communication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr)
118{
119 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
120 "%p Communication::Write (dst = %p, dst_len = %zu, timeout_usec = %u) connection = %p",
121 this, dst, dst_len, timeout_usec, m_connection_ap.get());
122
Eli Friedman8878f872010-07-09 22:53:18 +0000123 if (m_read_thread != LLDB_INVALID_HOST_THREAD)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000124 {
125 // We have a dedicated read thread that is getting data for us
126 size_t cached_bytes = GetCachedBytes (dst, dst_len);
127 if (cached_bytes > 0 || timeout_usec == 0)
128 {
129 status = eConnectionStatusSuccess;
130 return cached_bytes;
131 }
132
133 if (m_connection_ap.get() == NULL)
134 {
135 if (error_ptr)
136 error_ptr->SetErrorString("Invalid connection.");
137 status = eConnectionStatusNoConnection;
138 return 0;
139 }
140 // Set the timeout appropriately
141 TimeValue timeout_time;
142 if (timeout_usec != UINT32_MAX)
143 {
144 timeout_time = TimeValue::Now();
145 timeout_time.OffsetWithMicroSeconds (timeout_usec);
146 }
147
148 Listener listener ("Communication::Read");
149 listener.StartListeningForEvents (this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit);
150 EventSP event_sp;
151 while (listener.WaitForEvent (timeout_time.IsValid() ? &timeout_time : NULL, event_sp))
152 {
153 const uint32_t event_type = event_sp->GetType();
154 if (event_type & eBroadcastBitReadThreadGotBytes)
155 {
156 return GetCachedBytes (dst, dst_len);
157 }
158
159 if (event_type & eBroadcastBitReadThreadDidExit)
160 {
161 Disconnect (NULL);
162 break;
163 }
164 }
165 return 0;
166 }
167
168 // We aren't using a read thread, just read the data synchronously in this
169 // thread.
170 if (m_connection_ap.get())
171 {
172 status = m_connection_ap->BytesAvailable (timeout_usec, error_ptr);
173 if (status == eConnectionStatusSuccess)
174 return m_connection_ap->Read (dst, dst_len, status, error_ptr);
175 }
176
177 if (error_ptr)
178 error_ptr->SetErrorString("Invalid connection.");
179 status = eConnectionStatusNoConnection;
180 return 0;
181}
182
183
184size_t
185Communication::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr)
186{
187 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
188 "%p Communication::Write (src = %p, src_len = %zu) connection = %p",
189 this, src, src_len, m_connection_ap.get());
190
191 if (m_connection_ap.get())
192 return m_connection_ap->Write (src, src_len, status, error_ptr);
193
194 if (error_ptr)
195 error_ptr->SetErrorString("Invalid connection.");
196 status = eConnectionStatusNoConnection;
197 return 0;
198}
199
200
201bool
202Communication::StartReadThread (Error *error_ptr)
203{
204 if (m_read_thread != LLDB_INVALID_HOST_THREAD)
205 return true;
206
207 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
208 "%p Communication::StartReadThread ()", this);
209
210
211 char thread_name[1024];
212 snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>", m_broadcaster_name.AsCString());
213
Greg Clayton86c3f342010-09-15 05:19:45 +0000214 m_read_thread_enabled = true;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000215 m_read_thread = Host::ThreadCreate (thread_name, Communication::ReadThread, this, error_ptr);
Greg Clayton86c3f342010-09-15 05:19:45 +0000216 if (m_read_thread == LLDB_INVALID_HOST_THREAD)
217 m_read_thread_enabled = false;
Greg Clayton26661bc2010-07-23 15:43:25 +0000218 return m_read_thread_enabled;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000219}
220
221bool
222Communication::StopReadThread (Error *error_ptr)
223{
Eli Friedman8878f872010-07-09 22:53:18 +0000224 if (m_read_thread == LLDB_INVALID_HOST_THREAD)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000225 return true;
226
227 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
228 "%p Communication::StopReadThread ()", this);
229
230 m_read_thread_enabled = false;
231
232 BroadcastEvent (eBroadcastBitReadThreadShouldExit, NULL);
233
234 Host::ThreadCancel (m_read_thread, error_ptr);
235
236 return Host::ThreadJoin (m_read_thread, NULL, error_ptr);
Greg Clayton26661bc2010-07-23 15:43:25 +0000237 m_read_thread = LLDB_INVALID_HOST_THREAD;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000238}
239
240
241size_t
242Communication::GetCachedBytes (void *dst, size_t dst_len)
243{
244 Mutex::Locker locker(m_bytes_mutex);
245 if (m_bytes.size() > 0)
246 {
247 // If DST is NULL and we have a thread, then return the number
248 // of bytes that are available so the caller can call again
249 if (dst == NULL)
250 return m_bytes.size();
251
252 const size_t len = std::min<size_t>(dst_len, m_bytes.size());
253
Greg Clayton471b31c2010-07-20 22:52:08 +0000254 ::memcpy (dst, m_bytes.c_str(), len);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000255 m_bytes.erase(m_bytes.begin(), m_bytes.begin() + len);
256
257 return len;
258 }
259 return 0;
260}
261
262void
263Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast)
264{
265 lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION,
266 "%p Communication::AppendBytesToCache (src = %p, src_len = %zu, broadcast = %i)",
267 this, bytes, len, broadcast);
268 if (bytes == NULL || len == 0)
269 return;
270 if (m_callback)
271 {
272 // If the user registered a callback, then call it and do not broadcast
273 m_callback (m_callback_baton, bytes, len);
274 }
275 else
276 {
277 Mutex::Locker locker(m_bytes_mutex);
278 m_bytes.append ((const char *)bytes, len);
279 if (broadcast)
280 BroadcastEventIfUnique (eBroadcastBitReadThreadGotBytes);
281 }
282}
283
284size_t
285Communication::ReadFromConnection (void *dst, size_t dst_len, ConnectionStatus &status, Error *error_ptr)
286{
287 if (m_connection_ap.get())
288 return m_connection_ap->Read (dst, dst_len, status, error_ptr);
289 return 0;
290}
291
292
293bool
294Communication::ReadThreadIsRunning ()
295{
Eli Friedman8878f872010-07-09 22:53:18 +0000296 return m_read_thread != LLDB_INVALID_HOST_THREAD;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000297}
298
299void *
300Communication::ReadThread (void *p)
301{
302 Communication *comm = (Communication *)p;
303
304 Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION);
305
306 if (log)
307 log->Printf ("%p Communication::ReadThread () thread starting...", p);
308
309 uint8_t buf[1024];
310
311 Error error;
312 ConnectionStatus status = eConnectionStatusSuccess;
313 bool done = false;
314 while (!done && comm->m_read_thread_enabled)
315 {
316 status = comm->BytesAvailable (UINT32_MAX, &error);
317
318 if (status == eConnectionStatusSuccess)
319 {
320 size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), status, &error);
321 if (bytes_read > 0)
322 comm->AppendBytesToCache (buf, bytes_read, true);
323 }
324
325 switch (status)
326 {
327 case eConnectionStatusSuccess:
328 break;
329
330 case eConnectionStatusNoConnection: // No connection
331 case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection
332 done = true;
333 // Fall through...
334 default:
335 case eConnectionStatusError: // Check GetError() for details
336 case eConnectionStatusTimedOut: // Request timed out
337 error.LogIfError(log, "%p Communication::BytesAvailable () => status = %i", p, status);
338 break;
339 }
340 }
341 if (log)
342 log->Printf ("%p Communication::ReadThread () thread exiting...", p);
343
344 // Let clients know that this thread is exiting
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000345 comm->BroadcastEvent (eBroadcastBitReadThreadDidExit);
346 return NULL;
347}
348
349void
350Communication::SetReadThreadBytesReceivedCallback
351(
352 ReadThreadBytesReceived callback,
353 void *callback_baton
354)
355{
356 m_callback = callback;
357 m_callback_baton = callback_baton;
358}
359
360void
361Communication::SetConnection (Connection *connection)
362{
363 StopReadThread(NULL);
364 Disconnect (NULL);
365 m_connection_ap.reset(connection);
366}
Caroline Ticeceb6b132010-10-26 03:11:13 +0000367
368const char *
369Communication::ConnectionStatusAsCString (lldb::ConnectionStatus status)
370{
371 switch (status)
372 {
373 case eConnectionStatusSuccess: return "success";
374 case eConnectionStatusError: return "error";
375 case eConnectionStatusTimedOut: return "timed out";
376 case eConnectionStatusNoConnection: return "no connection";
377 case eConnectionStatusLostConnection: return "lost connection";
378 }
379
380 static char unknown_state_string[64];
381 snprintf(unknown_state_string, sizeof (unknown_state_string), "ConnectionStatus = %i", status);
382 return unknown_state_string;
383}