1
// This file is part of BOINC.
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
5
// BOINC is free software; you can redistribute it and/or modify it
6
// under the terms of the GNU Lesser General Public License
7
// as published by the Free Software Foundation,
8
// either version 3 of the License, or (at your option) any later version.
10
// BOINC is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
// See the GNU Lesser General Public License for more details.
15
// You should have received a copy of the GNU Lesser General Public License
16
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
18
#if defined(__GNUG__) && !defined(__APPLE__)
19
#pragma implementation "AsyncRPC.h"
22
#if !(defined(_WIN32) || (defined(__WXMAC__) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4)))
24
//#include "gui_rpc_client.h"
28
#include "BOINCGUIApp.h"
29
#include "MainDocument.h"
31
#include "BOINCBaseFrame.h"
32
#include "BOINCTaskBar.h"
33
#include "error_numbers.h"
34
#include "SkinManager.h"
35
#include "DlgEventLog.h"
38
extern bool s_bSkipExitConfirmation;
42
#ifdef HAVE_PTHREAD_MUTEXATTR_T
43
// on some systems pthread_mutexattr_settype() is not in the headers (but it is
44
// in the library, otherwise we wouldn't compile this code at all)
45
extern "C" int pthread_mutexattr_settype( pthread_mutexattr_t *, int );
48
BOINC_Mutex::BOINC_Mutex( wxMutexType mutexType )
53
case wxMUTEX_RECURSIVE:
54
// support recursive locks like Win32, i.e. a thread can lock a
55
// mutex which it had itself already locked
57
// unfortunately initialization of recursive mutexes is non
58
// portable, so try several methods
59
#ifdef HAVE_PTHREAD_MUTEXATTR_T
61
pthread_mutexattr_t attr;
62
pthread_mutexattr_init( &attr );
63
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
65
err = pthread_mutex_init( &m_mutex, &attr );
67
#elif defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
68
// we can use this only as initializer so we have to assign it
69
// first to a temp var - assigning directly to m_mutex wouldn't
72
pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
75
#else // no recursive mutexes
77
#endif // HAVE_PTHREAD_MUTEXATTR_T/...
81
wxFAIL_MSG( wxT("unknown mutex type") );
85
err = pthread_mutex_init( &m_mutex, NULL );
92
wxLogApiError( wxT("pthread_mutex_init()"), err );
96
BOINC_Mutex::~BOINC_Mutex()
100
int err = pthread_mutex_destroy( &m_mutex );
103
wxLogApiError( wxT("pthread_mutex_destroy()"), err );
108
wxMutexError BOINC_Mutex::Lock()
110
int err = pthread_mutex_lock( &m_mutex );
114
// only error checking mutexes return this value and so it's an
115
// unexpected situation -- hence use assert, not wxLogDebug
116
wxFAIL_MSG( wxT("mutex deadlock prevented") );
117
return wxMUTEX_DEAD_LOCK;
120
wxLogDebug( wxT("pthread_mutex_lock(): mutex not initialized.") );
124
return wxMUTEX_NO_ERROR;
127
wxLogApiError( wxT("pthread_mutex_lock()"), err );
130
return wxMUTEX_MISC_ERROR;
133
wxMutexError BOINC_Mutex::TryLock()
135
int err = pthread_mutex_trylock( &m_mutex );
139
// not an error: mutex is already locked, but we're prepared for this case
143
wxLogDebug( wxT("pthread_mutex_trylock(): mutex not initialized.") );
147
return wxMUTEX_NO_ERROR;
150
wxLogApiError( wxT("pthread_mutex_trylock()"), err );
153
return wxMUTEX_MISC_ERROR;
156
wxMutexError BOINC_Mutex::Unlock()
158
int err = pthread_mutex_unlock( &m_mutex );
162
// we don't own the mutex
163
return wxMUTEX_UNLOCKED;
166
wxLogDebug( wxT("pthread_mutex_unlock(): mutex not initialized.") );
170
return wxMUTEX_NO_ERROR;
173
wxLogApiError( wxT("pthread_mutex_unlock()"), err );
176
return wxMUTEX_MISC_ERROR;
180
// wxMac wxCondition has bugs, so use native UNIX implementation
182
BOINC_Condition::BOINC_Condition(BOINC_Mutex& mutex)
183
: m_BOINC_Mutex(mutex) {
186
err = pthread_cond_init(&m_cond, NULL);
187
mb_initOK = (err == 0);
190
BOINC_Condition::~BOINC_Condition() {
191
pthread_cond_destroy(&m_cond);
195
wxCondError BOINC_Condition::Wait(){
198
err = pthread_cond_wait(&m_cond, &m_BOINC_Mutex.m_mutex);
201
return wxCOND_NO_ERROR;
203
return wxCOND_INVALID;
205
return wxCOND_TIMEOUT;
207
return wxCOND_MISC_ERROR;
209
return wxCOND_NO_ERROR;
212
wxCondError BOINC_Condition::WaitTimeout(unsigned long milliseconds) {
214
wxLongLong curtime = wxGetLocalTimeMillis();
215
curtime += milliseconds;
216
wxLongLong temp = curtime / 1000;
217
int sec = temp.GetLo();
219
temp = curtime - temp;
220
int millis = temp.GetLo();
225
tspec.tv_nsec = millis * 1000L * 1000L;
227
err = pthread_cond_timedwait(&m_cond, &m_BOINC_Mutex.m_mutex, &tspec);
230
return wxCOND_NO_ERROR;
232
return wxCOND_INVALID;
234
return wxCOND_TIMEOUT;
236
return wxCOND_MISC_ERROR;
238
return wxCOND_NO_ERROR;
241
void BOINC_Condition::Signal() {
242
pthread_cond_signal(&m_cond);
245
void BOINC_Condition::Broadcast() {
246
pthread_cond_broadcast(&m_cond);
251
// Delay in milliseconds before showing AsyncRPCDlg
252
#define RPC_WAIT_DLG_DELAY 1500
253
// How often to check for events when minimized and waiting for Demand RPC
254
#define DELAY_WHEN_MINIMIZED 500
255
// Delay in milliseconds to allow thread to exit before killing it
256
#define RPC_KILL_DELAY 2000
258
ASYNC_RPC_REQUEST::ASYNC_RPC_REQUEST() {
263
ASYNC_RPC_REQUEST::~ASYNC_RPC_REQUEST() {
268
void ASYNC_RPC_REQUEST::clear() {
269
rpcType = (ASYNC_RPC_TYPE) 0;
270
which_rpc = (RPC_SELECTOR) 0;
276
completionTime = NULL;
277
RPCExecutionTime = NULL;
284
bool ASYNC_RPC_REQUEST::isSameAs(ASYNC_RPC_REQUEST& otherRequest) {
285
if (which_rpc != otherRequest.which_rpc) return false;
286
if (arg1 != otherRequest.arg1) return false;
287
if (exchangeBuf != otherRequest.exchangeBuf) return false;
288
if (arg2 != otherRequest.arg2) return false;
289
if (arg3 != otherRequest.arg3) return false;
290
if (arg4 != otherRequest.arg4) return false;
291
if (rpcType != otherRequest.rpcType) return false;
292
if (completionTime != otherRequest.completionTime) return false;
293
if (resultPtr != otherRequest.resultPtr) return false;
294
// OK if isActive and retval don't match.
299
AsyncRPC::AsyncRPC(CMainDocument *pDoc) {
304
AsyncRPC::~AsyncRPC() {}
307
int AsyncRPC::RPC_Wait(RPC_SELECTOR which_rpc, void *arg1, void *arg2,
308
void *arg3, void *arg4, bool hasPriority
310
ASYNC_RPC_REQUEST request;
313
request.which_rpc = which_rpc;
318
if (which_rpc == RPC_QUIT) {
319
request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH;
321
request.rpcType = RPC_TYPE_WAIT_FOR_COMPLETION;
323
request.RPCExecutionTime = NULL;
324
retval = m_pDoc->RequestRPC(request, hasPriority);
329
RPCThread::RPCThread(CMainDocument *pDoc,
330
BOINC_Mutex* pRPC_Thread_Mutex,
331
BOINC_Condition* pRPC_Thread_Condition,
332
BOINC_Mutex* pRPC_Request_Mutex,
333
BOINC_Condition* pRPC_Request_Condition)
336
m_pRPC_Thread_Mutex = pRPC_Thread_Mutex;
337
m_pRPC_Thread_Condition = pRPC_Thread_Condition;
338
m_pRPC_Request_Mutex = pRPC_Request_Mutex;
339
m_pRPC_Request_Condition = pRPC_Request_Condition;
342
void *RPCThread::Entry() {
344
CRPCFinishedEvent RPC_done_event( wxEVT_RPC_FINISHED );
345
ASYNC_RPC_REQUEST *current_request;
346
double startTime = 0;
347
wxMutexError mutexErr = wxMUTEX_NO_ERROR;
348
wxCondError condErr = wxCOND_NO_ERROR;
350
#ifndef NO_PER_THREAD_LOCALE
352
// On Windows, set all locales for this thread on a per-thread basis
353
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
354
setlocale(LC_ALL, "C");
356
// We initialize RPC_Thread_Locale to fix a compiler warning
357
locale_t RPC_Thread_Locale = LC_GLOBAL_LOCALE;
358
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4)
359
if (uselocale) // uselocale() is not available in Mac OS 10.3.9
362
// On Mac / Unix / Linux, set "C" locale for this thread only
363
RPC_Thread_Locale = newlocale(LC_ALL_MASK, "C", NULL);
364
uselocale(RPC_Thread_Locale);
366
#endif // ifndef __WXMSW__
367
#endif // ifndef NO_PER_THREAD_LOCALE
369
m_pRPC_Thread_Mutex->Lock();
370
m_pDoc->m_bRPCThreadIsReady = true;
372
// Wait for main thread to wake us
373
// This does the following:
374
// (1) Unlocks the Mutex and puts the RPC thread to sleep as an atomic operation.
375
// (2) On Signal from main thread: locks Mutex again and wakes the RPC thread.
376
condErr = m_pRPC_Thread_Condition->Wait();
377
wxASSERT(condErr == wxCOND_NO_ERROR);
379
if (m_pDoc->m_bShutDownRPCThread) {
380
#if !defined(NO_PER_THREAD_LOCALE) && !defined(__WXMSW__)
381
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4)
382
if (uselocale) // uselocale() is not available in Mac OS 10.3.9
385
uselocale(LC_GLOBAL_LOCALE);
386
freelocale(RPC_Thread_Locale);
389
m_pRPC_Thread_Mutex->Unlock(); // Just for safety - not really needed
390
// Tell CMainDocument that thread has gracefully ended
391
// We do this here because OnExit() is not called on Windows
392
m_pDoc->m_RPCThread = NULL;
396
current_request = m_pDoc->GetCurrentRPCRequest();
398
if (!current_request->isActive) continue; // Should never happen
400
if (current_request->RPCExecutionTime) {
403
retval = ProcessRPCRequest();
404
if (current_request->RPCExecutionTime) {
405
*(current_request->RPCExecutionTime) = dtime() - startTime;
408
current_request->retval = retval;
410
mutexErr = m_pRPC_Request_Mutex->Lock();
411
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
413
current_request->isActive = false;
414
wxPostEvent( wxTheApp, RPC_done_event );
416
// Signal() is ignored / discarded unless the main thread is
417
// currently blocked by m_pRPC_Request_Condition->Wait[Timeout]()
418
m_pRPC_Request_Condition->Signal();
420
mutexErr = m_pRPC_Request_Mutex->Unlock();
421
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
428
int RPCThread::ProcessRPCRequest() {
430
ASYNC_RPC_REQUEST *current_request = m_pDoc->GetCurrentRPCRequest();
432
switch (current_request->which_rpc) {
433
// RPC_SELECTORS with no arguments
434
case RPC_RUN_BENCHMARKS:
436
case RPC_NETWORK_AVAILABLE:
437
case RPC_PROJECT_ATTACH_FROM_FILE:
438
case RPC_READ_GLOBAL_PREFS_OVERRIDE:
439
case RPC_READ_CC_CONFIG:
442
// All others must have at least one argument
443
if (current_request->arg1 == NULL) {
449
switch (current_request->which_rpc) {
451
retval = (m_pDoc->rpcClient).authorize((const char*)(current_request->arg1));
453
case RPC_EXCHANGE_VERSIONS:
454
retval = (m_pDoc->rpcClient).exchange_versions(*(VERSION_INFO*)(current_request->arg1));
457
retval = (m_pDoc->rpcClient).get_state(*(CC_STATE*)(current_request->arg1));
459
case RPC_GET_RESULTS:
460
retval = (m_pDoc->rpcClient).get_results(*(RESULTS*)(current_request->arg1), *(bool*)(current_request->arg2));
462
case RPC_GET_FILE_TRANSFERS:
463
retval = (m_pDoc->rpcClient).get_file_transfers(*(FILE_TRANSFERS*)(current_request->arg1));
465
case RPC_GET_SIMPLE_GUI_INFO1:
466
retval = (m_pDoc->rpcClient).get_simple_gui_info(*(SIMPLE_GUI_INFO*)(current_request->arg1));
468
case RPC_GET_SIMPLE_GUI_INFO2:
469
// RPC_GET_SIMPLE_GUI_INFO2 is equivalent to doing both
470
// RPC_GET_PROJECT_STATUS1 and RPC_GET_RESULTS
471
retval = (m_pDoc->rpcClient).get_results(*(RESULTS*)(current_request->arg3));
473
retval = (m_pDoc->rpcClient).get_project_status(*(PROJECTS*)(current_request->arg1));
476
case RPC_GET_PROJECT_STATUS1:
477
retval = (m_pDoc->rpcClient).get_project_status(*(PROJECTS*)(current_request->arg1));
479
case RPC_GET_PROJECT_STATUS2:
480
retval = (m_pDoc->rpcClient).get_project_status(*(PROJECTS*)(current_request->arg1));
482
case RPC_GET_ALL_PROJECTS_LIST:
483
retval = (m_pDoc->rpcClient).get_all_projects_list(*(ALL_PROJECTS_LIST*)(current_request->arg1));
485
case RPC_GET_DISK_USAGE:
486
retval = (m_pDoc->rpcClient).get_disk_usage(*(DISK_USAGE*)(current_request->arg1));
488
case RPC_SHOW_GRAPHICS:
489
retval = (m_pDoc->rpcClient).show_graphics(
490
(const char*)(current_request->arg1),
491
(const char*)(current_request->arg2),
492
*(int*)(current_request->arg3),
493
*(DISPLAY_INFO*)(current_request->arg4)
497
retval = (m_pDoc->rpcClient).project_op(
498
*(PROJECT*)(current_request->arg1),
499
(const char*)(current_request->arg2)
502
case RPC_SET_RUN_MODE:
503
retval = (m_pDoc->rpcClient).set_run_mode(
504
*(int*)(current_request->arg1),
505
*(double*)(current_request->arg2)
508
case RPC_SET_GPU_MODE:
509
retval = (m_pDoc->rpcClient).set_gpu_mode(
510
*(int*)(current_request->arg1),
511
*(double*)(current_request->arg2)
514
case RPC_SET_NETWORK_MODE:
515
retval = (m_pDoc->rpcClient).set_network_mode(
516
*(int*)(current_request->arg1),
517
*(double*)(current_request->arg2)
520
case RPC_GET_SCREENSAVER_TASKS:
521
retval = (m_pDoc->rpcClient).get_screensaver_tasks(
522
*(int*)(current_request->arg1),
523
*(RESULTS*)(current_request->arg2)
526
case RPC_RUN_BENCHMARKS:
527
retval = (m_pDoc->rpcClient).run_benchmarks();
529
case RPC_SET_PROXY_SETTINGS:
530
retval = (m_pDoc->rpcClient).set_proxy_settings(*(GR_PROXY_INFO*)(current_request->arg1));
532
case RPC_GET_PROXY_SETTINGS:
533
retval = (m_pDoc->rpcClient).get_proxy_settings(*(GR_PROXY_INFO*)(current_request->arg1));
535
case RPC_GET_NOTICES:
536
retval = (m_pDoc->rpcClient).get_notices(
537
*(int*)(current_request->arg1),
538
*(NOTICES*)(current_request->arg2)
541
case RPC_GET_MESSAGES:
542
retval = (m_pDoc->rpcClient).get_messages(
543
*(int*)(current_request->arg1),
544
*(MESSAGES*)(current_request->arg2),
545
*(bool*)(current_request->arg3)
548
case RPC_FILE_TRANSFER_OP:
549
retval = (m_pDoc->rpcClient).file_transfer_op(
550
*(FILE_TRANSFER*)(current_request->arg1),
551
(const char*)(current_request->arg2)
555
retval = (m_pDoc->rpcClient).result_op(
556
*(RESULT*)(current_request->arg1),
557
(const char*)(current_request->arg2)
560
case RPC_GET_HOST_INFO:
561
retval = (m_pDoc->rpcClient).get_host_info(*(HOST_INFO*)(current_request->arg1));
564
retval = (m_pDoc->rpcClient).quit();
566
case RPC_ACCT_MGR_INFO:
567
retval = (m_pDoc->rpcClient).acct_mgr_info(*(ACCT_MGR_INFO*)(current_request->arg1));
569
case RPC_GET_STATISTICS:
570
retval = (m_pDoc->rpcClient).get_statistics(*(PROJECTS*)(current_request->arg1));
572
case RPC_NETWORK_AVAILABLE:
573
retval = (m_pDoc->rpcClient).network_available();
575
case RPC_GET_PROJECT_INIT_STATUS:
576
retval = (m_pDoc->rpcClient).get_project_init_status(*(PROJECT_INIT_STATUS*)(current_request->arg1));
578
case RPC_GET_PROJECT_CONFIG:
579
retval = (m_pDoc->rpcClient).get_project_config(*(std::string*)(current_request->arg1));
581
case RPC_GET_PROJECT_CONFIG_POLL:
582
retval = (m_pDoc->rpcClient).get_project_config_poll(*(PROJECT_CONFIG*)(current_request->arg1));
584
case RPC_LOOKUP_ACCOUNT:
585
retval = (m_pDoc->rpcClient).lookup_account(*(ACCOUNT_IN*)(current_request->arg1));
587
case RPC_LOOKUP_ACCOUNT_POLL:
588
retval = (m_pDoc->rpcClient).lookup_account_poll(*(ACCOUNT_OUT*)(current_request->arg1));
590
case RPC_CREATE_ACCOUNT:
591
retval = (m_pDoc->rpcClient).create_account(*(ACCOUNT_IN*)(current_request->arg1));
593
case RPC_CREATE_ACCOUNT_POLL:
594
retval = (m_pDoc->rpcClient).create_account_poll(*(ACCOUNT_OUT*)(current_request->arg1));
596
case RPC_PROJECT_ATTACH:
597
retval = (m_pDoc->rpcClient).project_attach(
598
(const char*)(current_request->arg1),
599
(const char*)(current_request->arg2),
600
(const char*)(current_request->arg3)
603
case RPC_PROJECT_ATTACH_FROM_FILE:
604
retval = (m_pDoc->rpcClient).project_attach_from_file();
606
case RPC_PROJECT_ATTACH_POLL:
607
retval = (m_pDoc->rpcClient).project_attach_poll(*(PROJECT_ATTACH_REPLY*)(current_request->arg1));
609
case RPC_ACCT_MGR_RPC:
610
retval = (m_pDoc->rpcClient).acct_mgr_rpc(
611
(const char*)(current_request->arg1),
612
(const char*)(current_request->arg2),
613
(const char*)(current_request->arg3),
614
(bool)(current_request->arg4 != NULL)
617
case RPC_ACCT_MGR_RPC_POLL:
618
retval = (m_pDoc->rpcClient).acct_mgr_rpc_poll(*(ACCT_MGR_RPC_REPLY*)(current_request->arg1));
620
case RPC_GET_NEWER_VERSION:
621
retval = (m_pDoc->rpcClient).get_newer_version(
622
*(std::string*)(current_request->arg1),
623
*(std::string*)(current_request->arg2)
626
case RPC_READ_GLOBAL_PREFS_OVERRIDE:
627
retval = (m_pDoc->rpcClient).read_global_prefs_override();
629
case RPC_READ_CC_CONFIG:
630
retval = (m_pDoc->rpcClient).read_cc_config();
632
case RPC_GET_CC_STATUS:
633
retval = (m_pDoc->rpcClient).get_cc_status(*(CC_STATUS*)(current_request->arg1));
635
case RPC_GET_GLOBAL_PREFS_FILE:
636
retval = (m_pDoc->rpcClient).get_global_prefs_file(*(std::string*)(current_request->arg1));
638
case RPC_GET_GLOBAL_PREFS_WORKING:
639
retval = (m_pDoc->rpcClient).get_global_prefs_working(*(std::string*)(current_request->arg1));
641
case RPC_GET_GLOBAL_PREFS_WORKING_STRUCT:
642
retval = (m_pDoc->rpcClient).get_global_prefs_working_struct(
643
*(GLOBAL_PREFS*)(current_request->arg1),
644
*(GLOBAL_PREFS_MASK*)(current_request->arg2)
647
case RPC_GET_GLOBAL_PREFS_OVERRIDE:
648
retval = (m_pDoc->rpcClient).get_global_prefs_override(*(std::string*)(current_request->arg1));
650
case RPC_SET_GLOBAL_PREFS_OVERRIDE:
651
retval = (m_pDoc->rpcClient).set_global_prefs_override(*(std::string*)(current_request->arg1));
653
case RPC_GET_GLOBAL_PREFS_OVERRIDE_STRUCT:
654
retval = (m_pDoc->rpcClient).get_global_prefs_override_struct(
655
*(GLOBAL_PREFS*)(current_request->arg1),
656
*(GLOBAL_PREFS_MASK*)(current_request->arg2)
659
case RPC_SET_GLOBAL_PREFS_OVERRIDE_STRUCT:
660
retval = (m_pDoc->rpcClient).set_global_prefs_override_struct(
661
*(GLOBAL_PREFS*)(current_request->arg1),
662
*(GLOBAL_PREFS_MASK*)(current_request->arg2)
666
retval = (m_pDoc->rpcClient).set_debts(*(std::vector<PROJECT>*)(current_request->arg1));
676
// TODO: combine RPC requests for different buffers, then just copy the buffer.
678
int CMainDocument::RequestRPC(ASYNC_RPC_REQUEST& request, bool hasPriority) {
679
std::vector<ASYNC_RPC_REQUEST>::iterator iter;
681
int response = wxID_OK;
682
wxMutexError mutexErr = wxMUTEX_NO_ERROR;
683
long delayTimeRemaining, timeToSleep;
686
if (!m_RPCThread) return -1;
688
if ( (request.rpcType < RPC_TYPE_WAIT_FOR_COMPLETION) ||
689
(request.rpcType >= NUM_RPC_TYPES) ) {
694
// If we are quitting, cancel any pending RPCs
695
if (request.which_rpc == RPC_QUIT) {
696
if (current_rpc_request.isActive) {
697
RPC_requests.erase(RPC_requests.begin()+1, RPC_requests.end());
700
RPC_requests.clear();
704
// Check if a duplicate request is already on the queue
705
for (iter=RPC_requests.begin(); iter!=RPC_requests.end(); iter++) {
706
if (iter->isSameAs(request)) {
711
if ((request.rpcType == RPC_TYPE_WAIT_FOR_COMPLETION) && (request.resultPtr == NULL)) {
712
request.resultPtr = &retval;
716
// We may want to set hasPriority for some user-initiated events.
717
// Since the user is waiting, insert this at head of request queue.
718
// As of 8/14/08, hasPriority is never set true, so hasn't been tested.
719
iter = RPC_requests.insert(RPC_requests.begin(), request);
721
RPC_requests.push_back(request);
724
// Start this RPC if no other RPC is already in progress.
725
if (RPC_requests.size() == 1) {
726
// Wait for thread to unlock mutex with m_pRPC_Thread_Condition->Wait()
727
mutexErr = m_pRPC_Thread_Mutex->Lock(); // Blocks until thread unlocks the mutex
728
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
730
// Make sure activation is an atomic operation
731
request.isActive = false;
732
current_rpc_request = request;
733
current_rpc_request.isActive = true;
735
m_pRPC_Thread_Condition->Signal(); // Unblock the thread
737
// m_pRPC_Thread_Condition->Wait() will Lock() the mutex upon receiving Signal(),
738
// causing it to block again if we still have our lock on the mutex.
739
mutexErr = m_pRPC_Thread_Mutex->Unlock();
740
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
743
// If this is a user-initiated event wait for completion but show
744
// a dialog allowing the user to cancel.
745
if (request.rpcType == RPC_TYPE_WAIT_FOR_COMPLETION) {
746
// TODO: proper handling if a second user request is received while first is pending ??
747
if (m_bWaitingForRPC) {
748
wxLogMessage(wxT("Second user RPC request while another was pending"));
752
// Don't show dialog if RPC completes before RPC_WAIT_DLG_DELAY
753
// or while BOINC is minimized
754
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
755
wxStopWatch Dlgdelay = wxStopWatch();
756
m_RPCWaitDlg = new AsyncRPCDlg();
757
m_bWaitingForRPC = true;
759
// Allow RPC_WAIT_DLG_DELAY seconds for Demand RPC to complete before
760
// displaying "Please Wait" dialog, but keep checking for completion.
761
delayTimeRemaining = RPC_WAIT_DLG_DELAY;
763
if (delayTimeRemaining >= 0) { // Prevent overflow if minimized for a very long time
764
delayTimeRemaining = RPC_WAIT_DLG_DELAY - Dlgdelay.Time();
768
shown = pFrame->IsShown();
774
if (delayTimeRemaining <= 0) break; // Display the Please Wait dialog
775
timeToSleep = delayTimeRemaining;
777
// Don't show dialog while Manager is minimized, but do
778
// process events so user can maximize the manager.
780
// NOTE: CBOINCGUIApp::FilterEvent() discards those events
781
// which might cause posting of more RPC requests while
782
// we are in this loop, to prevent undesirable recursion.
783
// Since the manager is minimized, we don't have to worry about
784
// discarding crucial drawing or command events.
785
// The filter does allow the the Open Manager menu item from
786
// the system tray icon and wxEVT_RPC_FINISHED event.
788
timeToSleep = DELAY_WHEN_MINIMIZED; // Allow user to maximize Manager
789
wxSafeYield(NULL, true);
792
// OnRPCComplete() clears m_bWaitingForRPC if RPC completed
793
if (! m_bWaitingForRPC) {
797
mutexErr = m_pRPC_Request_Mutex->Lock();
798
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
800
// Simulate handling of CRPCFinishedEvent but don't allow any other
801
// events (so no user activity) to prevent undesirable recursion.
802
// Since we don't need to filter and discard events, they remain on
803
// the queue until it is safe to process them.
804
// Allow RPC thread to run while we wait for it.
805
if (!current_rpc_request.isActive) {
806
mutexErr = m_pRPC_Request_Mutex->Unlock();
807
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
809
HandleCompletedRPC();
813
// Wait for RPC thread to wake us
814
// This does the following:
815
// (1) Unlocks the Mutex and puts the main thread to sleep as an atomic operation.
816
// (2) On Signal from RPC thread: locks Mutex again and wakes the main thread.
817
m_pRPC_Request_Condition->WaitTimeout(timeToSleep);
819
mutexErr = m_pRPC_Request_Mutex->Unlock();
820
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
823
// Demand RPC has taken longer than RPC_WAIT_DLG_DELAY seconds and
824
// Manager is not minimized, so display the "Please Wait" dialog
825
// with a Cancel button. If the RPC does complete while the dialog
826
// is up, HandleCompletedRPC() will call EndModal with wxID_OK.
828
// NOTE: the Modal dialog permits processing of all events, but
829
// CBOINCGUIApp::FilterEvent() blocks those events which might cause
830
// posting of more RPC requests while in this dialog, to prevent
831
// undesirable recursion.
834
response = m_RPCWaitDlg->ShowModal();
835
// Remember time the dialog was closed for use by RunPeriodicRPCs()
836
m_dtLasAsyncRPCDlgTime = wxDateTime::Now();
837
if (response != wxID_OK) {
838
// TODO: If user presses Cancel in Please Wait dialog but request
839
// has not yet been started, should we just remove it from queue?
840
// If we make that change, should we also add a separate menu item
841
// to reset the RPC connection (or does one already exist)?
844
// If the RPC continues to get data after we return to
845
// our caller, it may try to write into a buffer or struct
846
// which the caller has already deleted. To prevent this,
847
// we close the socket (disconnect) and kill the RPC thread.
848
// This is ugly but necessary. We must then reconnect and
849
// start a new RPC thread.
850
if (current_rpc_request.isActive) {
851
current_rpc_request.isActive = false;
853
RPC_requests.clear();
854
current_rpc_request.clear();
855
m_bNeedRefresh = false;
856
m_bNeedTaskBarRefresh = false;
858
// We will be reconnected to the same client (if possible) by
859
// CBOINCDialUpManager::OnPoll() and CNetworkConnection::Poll().
860
m_pNetworkConnection->SetStateDisconnected();
862
if (response == wxID_EXIT) {
863
pFrame = wxGetApp().GetFrame();
864
wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED, wxID_EXIT);
865
s_bSkipExitConfirmation = true;
866
pFrame->AddPendingEvent(evt);
870
m_RPCWaitDlg->Destroy();
873
m_bWaitingForRPC = false;
880
void CMainDocument::KillRPCThread() {
881
wxMutexError mutexErr = wxMUTEX_NO_ERROR;
888
m_bNeedRefresh = false;
889
m_bNeedTaskBarRefresh = false;
891
rpcClient.close(); // Abort any async RPC in progress (in case hung)
893
// On some platforms, Delete() takes effect only when thread calls TestDestroy()
894
// Wait for thread to unlock mutex with m_pRPC_Thread_Condition->Wait()
895
mutexErr = m_pRPC_Thread_Mutex->Lock(); // Blocks until thread unlocks the mutex
896
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
898
m_bShutDownRPCThread = true;
899
m_pRPC_Thread_Condition->Signal(); // Unblock the thread
901
mutexErr = m_pRPC_Thread_Mutex->Unlock(); // Release the mutex so thread can lock it
902
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
904
RPC_requests.clear();
905
current_rpc_request.clear();
907
// Wait up to RPC_KILL_DELAY milliseconds for thread to exit on its own
908
for (i=0; i< RPC_KILL_DELAY; ++i) {
909
boinc_sleep(.001); // Defer to RPC thread for 1 millisecond
911
return; // RPC thread sets m_RPCThread to NULL when it exits
914
// Thread failed to exit, so forcefully kill it
919
void CMainDocument::OnRPCComplete(CRPCFinishedEvent&) {
920
HandleCompletedRPC();
924
void CMainDocument::HandleCompletedRPC() {
926
wxMutexError mutexErr = wxMUTEX_NO_ERROR;
927
int i, n, requestIndex = -1;
928
bool stillWaitingForPendingRequests = false;
930
if (!m_RPCThread) return;
932
if (current_rpc_request.isActive) return;
934
// We can get here either via a CRPCFinishedEvent event posted
935
// by the RPC thread or by a call from RequestRPC. If we were
936
// called from RequestRPC, the CRPCFinishedEvent will still be
937
// on the event queue, so we get called twice. Check for this here.
938
if (current_rpc_request.which_rpc == 0) return; // already handled by a call from RequestRPC
940
// Find our completed request in the queue
941
n = (int) RPC_requests.size();
942
for (i=0; i<n; ++i) {
943
if (RPC_requests[i].isSameAs(current_rpc_request)) {
946
if (RPC_requests[i].rpcType == RPC_TYPE_WAIT_FOR_COMPLETION) {
947
stillWaitingForPendingRequests = true;
952
if (! stillWaitingForPendingRequests) {
954
if (m_RPCWaitDlg->IsShown()) {
955
m_RPCWaitDlg->EndModal(wxID_OK);
957
m_RPCWaitDlg->Destroy();
960
m_bWaitingForRPC = false;
963
if (requestIndex >= 0) {
964
// Remove completed request from the queue
965
RPC_requests.erase(RPC_requests.begin()+requestIndex);
968
retval = current_rpc_request.retval;
971
if (current_rpc_request.completionTime) {
972
*(current_rpc_request.completionTime) = wxDateTime::Now();
975
if (current_rpc_request.resultPtr) {
976
*(current_rpc_request.resultPtr) = retval;
981
if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_REFRESH_AFTER) {
983
m_bNeedRefresh = true;
987
if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER) {
989
m_bNeedTaskBarRefresh = true;
993
switch (current_rpc_request.which_rpc) {
995
if (current_rpc_request.exchangeBuf && !retval) {
996
CC_STATE* arg1 = (CC_STATE*)current_rpc_request.arg1;
997
CC_STATE* exchangeBuf = (CC_STATE*)current_rpc_request.exchangeBuf;
998
arg1->projects.swap(exchangeBuf->projects);
999
arg1->apps.swap(exchangeBuf->apps);
1000
arg1->app_versions.swap(exchangeBuf->app_versions);
1001
arg1->wus.swap(exchangeBuf->wus);
1002
arg1->results.swap(exchangeBuf->results);
1003
exchangeBuf->global_prefs = arg1->global_prefs;
1004
exchangeBuf->version_info = arg1->version_info;
1005
exchangeBuf->executing_as_daemon = arg1->executing_as_daemon;
1006
exchangeBuf->have_cuda = arg1->have_cuda;
1007
exchangeBuf->have_ati = arg1->have_ati;
1010
case RPC_GET_RESULTS:
1011
if (current_rpc_request.exchangeBuf && !retval) {
1012
RESULTS* arg1 = (RESULTS*)current_rpc_request.arg1;
1013
RESULTS* exchangeBuf = (RESULTS*)current_rpc_request.exchangeBuf;
1014
arg1->results.swap(exchangeBuf->results);
1017
case RPC_GET_FILE_TRANSFERS:
1018
if (current_rpc_request.exchangeBuf && !retval) {
1019
FILE_TRANSFERS* arg1 = (FILE_TRANSFERS*)current_rpc_request.arg1;
1020
FILE_TRANSFERS* exchangeBuf = (FILE_TRANSFERS*)current_rpc_request.exchangeBuf;
1021
arg1->file_transfers.swap(exchangeBuf->file_transfers);
1024
case RPC_GET_SIMPLE_GUI_INFO2:
1026
retval = CopyProjectsToStateBuffer(*(PROJECTS*)(current_rpc_request.arg1), *(CC_STATE*)(current_rpc_request.arg2));
1028
if (current_rpc_request.exchangeBuf && !retval) {
1029
RESULTS* arg3 = (RESULTS*)current_rpc_request.arg3;
1030
RESULTS* exchangeBuf = (RESULTS*)current_rpc_request.exchangeBuf;
1031
arg3->results.swap(exchangeBuf->results);
1034
case RPC_GET_PROJECT_STATUS1:
1036
retval = CopyProjectsToStateBuffer(*(PROJECTS*)(current_rpc_request.arg1), *(CC_STATE*)(current_rpc_request.arg2));
1039
case RPC_GET_ALL_PROJECTS_LIST:
1040
if (current_rpc_request.exchangeBuf && !retval) {
1041
ALL_PROJECTS_LIST* arg1 = (ALL_PROJECTS_LIST*)current_rpc_request.arg1;
1042
ALL_PROJECTS_LIST* exchangeBuf = (ALL_PROJECTS_LIST*)current_rpc_request.exchangeBuf;
1043
arg1->projects.swap(exchangeBuf->projects);
1046
case RPC_GET_DISK_USAGE:
1047
if (current_rpc_request.exchangeBuf && !retval) {
1048
DISK_USAGE* arg1 = (DISK_USAGE*)current_rpc_request.arg1;
1049
DISK_USAGE* exchangeBuf = (DISK_USAGE*)current_rpc_request.exchangeBuf;
1050
arg1->projects.swap(exchangeBuf->projects);
1051
exchangeBuf->d_total = arg1->d_total;
1052
exchangeBuf->d_free = arg1->d_free;
1053
exchangeBuf->d_boinc = arg1->d_boinc;
1054
exchangeBuf->d_allowed = arg1->d_allowed;
1057
case RPC_GET_NOTICES:
1058
if (current_rpc_request.exchangeBuf && !retval) {
1059
NOTICES* arg2 = (NOTICES*)current_rpc_request.arg2;
1060
NOTICES* exchangeBuf = (NOTICES*)current_rpc_request.exchangeBuf;
1061
arg2->notices.swap(exchangeBuf->notices);
1064
CachedNoticeUpdate(); // Call this only when notice buffer is stable
1067
case RPC_GET_MESSAGES:
1068
if (current_rpc_request.exchangeBuf && !retval) {
1069
MESSAGES* arg2 = (MESSAGES*)current_rpc_request.arg2;
1070
MESSAGES* exchangeBuf = (MESSAGES*)current_rpc_request.exchangeBuf;
1071
arg2->messages.swap(exchangeBuf->messages);
1074
CachedMessageUpdate(); // Call this only when message buffer is stable
1077
case RPC_GET_HOST_INFO:
1078
if (current_rpc_request.exchangeBuf && !retval) {
1079
HOST_INFO* arg1 = (HOST_INFO*)current_rpc_request.arg1;
1080
HOST_INFO* exchangeBuf = (HOST_INFO*)current_rpc_request.exchangeBuf;
1081
*exchangeBuf = *arg1;
1084
case RPC_GET_STATISTICS:
1085
if (current_rpc_request.exchangeBuf && !retval) {
1086
PROJECTS* arg1 = (PROJECTS*)current_rpc_request.arg1;
1087
PROJECTS* exchangeBuf = (PROJECTS*)current_rpc_request.exchangeBuf;
1088
arg1->projects.swap(exchangeBuf->projects);
1092
case RPC_GET_CC_STATUS:
1093
if (current_rpc_request.exchangeBuf && !retval) {
1094
CC_STATUS* arg1 = (CC_STATUS*)current_rpc_request.arg1;
1095
CC_STATUS* exchangeBuf = (CC_STATUS*)current_rpc_request.exchangeBuf;
1096
*exchangeBuf = *arg1;
1099
case RPC_ACCT_MGR_INFO:
1100
if (current_rpc_request.exchangeBuf && !retval) {
1101
ACCT_MGR_INFO* arg1 = (ACCT_MGR_INFO*)current_rpc_request.arg1;
1102
ACCT_MGR_INFO* exchangeBuf = (ACCT_MGR_INFO*)current_rpc_request.exchangeBuf;
1103
*exchangeBuf = *arg1;
1107
// We don't support double buffering for other RPC calls
1108
wxASSERT(current_rpc_request.exchangeBuf == NULL);
1113
if (current_rpc_request.resultPtr) {
1114
// In case post-processing changed retval
1115
*(current_rpc_request.resultPtr) = retval;
1118
// We must call ProcessEvent() rather than AddPendingEvent() here to
1119
// guarantee integrity of data when other events are handled (such as
1120
// Abort, Suspend/Resume, Show Graphics, Update, Detach, Reset, No
1121
// New Work, etc.) Otherwise, if one of those events is pending it
1122
// might be processed first, and the data in the selected rows may not
1123
// match the data which the user selected if any rows were added or
1124
// deleted due to the RPC.
1125
// The refresh event called here adjusts the selections to fix any
1126
// such mismatch before other pending events are processed.
1128
// However, the refresh code may itself request a Demand RPC, which
1129
// would cause undesirable recursion if we are already waiting for
1130
// another Demand RPC to complete. In that case, we defer the refresh
1131
// until all pending Demand RPCs have been done.
1133
if (m_bNeedRefresh && !m_bWaitingForRPC) {
1134
m_bNeedRefresh = false;
1135
// We must get the frame immediately before using it,
1136
// since it may have been changed by SetActiveGUI().
1137
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
1139
CFrameEvent event(wxEVT_FRAME_REFRESHVIEW, pFrame);
1140
pFrame->ProcessEvent(event);
1144
if (m_bNeedTaskBarRefresh && !m_bWaitingForRPC) {
1145
m_bNeedTaskBarRefresh = false;
1146
CTaskBarIcon* pTaskbar = wxGetApp().GetTaskBarIcon();
1148
CTaskbarEvent event(wxEVT_TASKBAR_REFRESH, pTaskbar);
1149
pTaskbar->ProcessEvent(event);
1153
if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_REFRESH_EVENT_LOG_AFTER) {
1154
CDlgEventLog* eventLog = wxGetApp().GetEventLog();
1156
eventLog->OnRefresh();
1160
current_rpc_request.clear();
1162
// Start the next RPC request.
1163
// We can't start this until finished processing the previous RPC's
1164
// event because the two requests may write into the same buffer.
1165
if (RPC_requests.size() > 0) {
1166
// Wait for thread to unlock mutex with m_pRPC_Thread_Condition->Wait()
1167
mutexErr = m_pRPC_Thread_Mutex->Lock(); // Blocks until thread unlocks the mutex
1168
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
1170
// Make sure activation is an atomic operation
1171
RPC_requests[0].isActive = false;
1172
current_rpc_request = RPC_requests[0];
1173
current_rpc_request.isActive = true;
1175
m_pRPC_Thread_Condition->Signal(); // Unblock the thread
1177
// m_pRPC_Thread_Condition->Wait() will Lock() the mutex upon receiving Signal(),
1178
// causing it to block again if we still have our lock on the mutex.
1179
mutexErr = m_pRPC_Thread_Mutex->Unlock();
1180
wxASSERT(mutexErr == wxMUTEX_NO_ERROR);
1185
int CMainDocument::CopyProjectsToStateBuffer(PROJECTS& p, CC_STATE& state) {
1188
PROJECT* state_project = NULL;
1191
for (i=0; i<state.projects.size(); i++) {
1192
state_project = state.projects[i];
1193
state_project->flag_for_delete = true;
1196
for (i=0; i<p.projects.size(); i++) {
1197
state_project = state.lookup_project(p.projects[i]->master_url);
1198
if (state_project && (!strcmp(p.projects[i]->master_url, state_project->master_url))) {
1199
// Because the CC_STATE contains several pointers to each element of the
1200
// CC_STATE::projects vector, we must update these elements in place.
1201
*state_project = *(p.projects[i]);
1202
state_project->flag_for_delete = false;
1204
retval = ERR_NOT_FOUND;
1209
// Anything need to be deleted?
1211
for (i=0; i<state.projects.size(); i++) {
1212
state_project = state.projects[i];
1213
if (state_project->flag_for_delete) {
1214
retval = ERR_FILE_MISSING;
1223
BEGIN_EVENT_TABLE(AsyncRPCDlg, wxDialog)
1224
EVT_BUTTON(wxID_EXIT, AsyncRPCDlg::OnExit)
1227
IMPLEMENT_CLASS(AsyncRPCDlg, wxDialog)
1229
AsyncRPCDlg::AsyncRPCDlg() : wxDialog( NULL, wxID_ANY, wxT(""), wxDefaultPosition ) {
1230
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
1231
wxString exit_label;
1232
wxASSERT(pSkinAdvanced);
1234
wxString message = wxString(_("Communicating with BOINC client. Please wait ..."));
1237
exit_label.Printf(_("&Quit %s"), pSkinAdvanced->GetApplicationName().c_str());
1239
exit_label.Printf(_("E&xit %s"), pSkinAdvanced->GetApplicationName().c_str());
1242
wxString strCaption;
1243
strCaption.Printf(_("%s - Communication"), pSkinAdvanced->GetApplicationName().c_str());
1244
SetTitle(strCaption.c_str());
1246
wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
1247
wxBoxSizer *icon_text = new wxBoxSizer( wxHORIZONTAL );
1249
icon_text->Add( CreateTextSizer( message ), 0, wxALIGN_CENTER | wxLEFT, 10 );
1250
topsizer->Add( icon_text, 1, wxCENTER | wxLEFT|wxRIGHT|wxTOP, 10 );
1252
wxStdDialogButtonSizer *sizerBtn = CreateStdDialogButtonSizer(0);
1254
wxButton* exitbutton = new wxButton;
1255
exitbutton->Create( this, wxID_EXIT, exit_label, wxDefaultPosition, wxDefaultSize, 0 );
1256
sizerBtn->Add(exitbutton, 0, wxLEFT|wxRIGHT|wxALL, 5);
1258
wxButton* cancelbutton = new wxButton;
1259
cancelbutton->Create( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
1260
sizerBtn->Add(cancelbutton, 0, wxLEFT|wxRIGHT|wxALL, 5);
1263
topsizer->Add(sizerBtn, 0, wxEXPAND | wxALL, 10 );
1265
SetAutoLayout( true );
1266
SetSizer( topsizer );
1268
topsizer->SetSizeHints( this );
1269
topsizer->Fit( this );
1270
wxSize size( GetSize() );
1271
if (size.x < size.y*3/2)
1273
size.x = size.y*3/2;
1277
Centre( wxBOTH | wxCENTER_FRAME);
1281
void AsyncRPCDlg::OnExit(wxCommandEvent& WXUNUSED(eventUnused)) {
1282
EndModal(wxID_EXIT);
1288
/// For testing: triggered by Advanced / Options menu item.
1289
void CMainDocument::TestAsyncRPC() {
1290
ALL_PROJECTS_LIST pl;
1291
ASYNC_RPC_REQUEST request;
1292
wxDateTime completionTime = wxDateTime((time_t)0);
1293
int req_retval = 0, rpc_result = 0;
1295
completionTime.ResetTime();
1297
request.which_rpc = RPC_GET_ALL_PROJECTS_LIST;
1299
request.exchangeBuf = NULL;
1300
request.arg2 = NULL;
1301
request.arg3 = NULL;
1302
request.arg4 = NULL;
1303
request.rpcType = RPC_TYPE_WAIT_FOR_COMPLETION;
1304
request.completionTime = &completionTime;
1305
// request.result = NULL;
1306
request.resultPtr = &rpc_result; // For testing async RPCs
1307
request.isActive = false;
1309
//retval = rpcClient.get_all_projects_list(pl);
1311
req_retval = RequestRPC(request, true);
1313
wxString s = completionTime.FormatTime();
1314
wxLogMessage(wxT("Completion time = %s"), s.c_str());
1315
wxLogMessage(wxT("RequestRPC returned %d\n"), req_retval);
1316
::wxSafeYield(NULL, true); // Allow processing of RPC_FINISHED event
1317
wxLogMessage(wxT("rpcClient.get_all_projects_list returned %d\n"), rpc_result);