From 31e6ea7f5d5b3d73f4084261465142c9fe0fb894 Mon Sep 17 00:00:00 2001 From: s_nakamoto Date: Fri, 13 Nov 2009 01:23:08 +0000 Subject: [PATCH] monitor ThreadSocketHandler and terminate and restart if hung, convert _beginthread to CreateThread wrapper, disconnect inactive connections, ping, break up long messages to speed up initial download, better priorities for initiating connections, track how many nodes have requested our blocks and transactions, status #/offline and warning message on unsent blocks, minimize on close as separate option -- linux-test5 --- build-msw.txt | 2 +- build-unix.txt | 1 + db.cpp | 9 +- headers.h | 1 - irc.cpp | 5 +- main.cpp | 144 +++++++++++++++++---- main.h | 25 ++-- net.cpp | 342 +++++++++++++++++++++++++++++++------------------ net.h | 25 ++-- ui.cpp | 137 +++++++++++++------- ui.h | 2 +- uibase.cpp | 16 +-- uibase.h | 92 ++++++------- uiproject.fbp | 121 ++++++++--------- util.cpp | 48 ++++--- util.h | 105 ++++++++++++--- 16 files changed, 684 insertions(+), 391 deletions(-) diff --git a/build-msw.txt b/build-msw.txt index eb6348bad..a159a71ae 100644 --- a/build-msw.txt +++ b/build-msw.txt @@ -8,7 +8,7 @@ the OpenSSL Toolkit (http://www.openssl.org/). This product includes cryptographic software written by Eric Young (eay@cryptsoft.com). - WINDOWS BUILD NOTES +WINDOWS BUILD NOTES Compilers Supported diff --git a/build-unix.txt b/build-unix.txt index 6b29e1e5c..ceb61ad54 100644 --- a/build-unix.txt +++ b/build-unix.txt @@ -13,6 +13,7 @@ UNIX BUILD NOTES Dependencies ------------ +Install the dev files for the shared libraries: apt-get install build-essential apt-get install libgtk2.0-dev apt-get install libssl-dev diff --git a/db.cpp b/db.cpp index 61d602533..a9c42880a 100644 --- a/db.cpp +++ b/db.cpp @@ -505,6 +505,13 @@ bool CWalletDB::LoadWallet(vector& vchDefaultKeyRet) { vchDefaultKeyRet.clear(); + // Modify defaults +#ifndef __WXMSW__ + // Reports that tray icon can disappear on gnome, leaving no way to access the program + fMinimizeToTray = false; + fMinimizeOnClose = false; +#endif + //// todo: shouldn't we catch exceptions and try to recover and continue? CRITICAL_BLOCK(cs_mapKeys) CRITICAL_BLOCK(cs_mapWallet) @@ -638,7 +645,7 @@ bool LoadWallet(bool& fFirstRunRet) CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); } - _beginthread(ThreadFlushWalletDB, 0, NULL); + CreateThread(ThreadFlushWalletDB, NULL); return true; } diff --git a/headers.h b/headers.h index 76881bb93..22bb830f7 100644 --- a/headers.h +++ b/headers.h @@ -75,7 +75,6 @@ #include #include #include -#include #include #endif diff --git a/irc.cpp b/irc.cpp index 3b232caea..8432c6d14 100644 --- a/irc.cpp +++ b/irc.cpp @@ -54,7 +54,7 @@ static bool Send(SOCKET hSocket, const char* pszSend) const char* pszEnd = psz + strlen(psz); while (psz < pszEnd) { - int ret = send(hSocket, psz, pszEnd - psz, 0); + int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL); if (ret < 0) return false; psz += ret; @@ -156,7 +156,7 @@ bool Wait(int nSeconds) void ThreadIRCSeed(void* parg) { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + SetThreadPriority(THREAD_PRIORITY_NORMAL); int nErrorWait = 10; int nRetryWait = 10; @@ -256,6 +256,7 @@ void ThreadIRCSeed(void* parg) CAddress addr; if (DecodeAddress(pszName, addr)) { + addr.nTime = GetAdjustedTime() - 51 * 60; CAddrDB addrdb; if (AddAddress(addrdb, addr)) printf("IRC got new address\n"); diff --git a/main.cpp b/main.cpp index b957027f0..13a9f9b72 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,9 @@ map > mapPubKeys; CCriticalSection cs_mapKeys; CKey keyUser; +map mapRequestCount; +CCriticalSection cs_mapRequestCount; + // Settings int fGenerateBitcoins = false; int64 nTransactionFee = 0; @@ -274,7 +277,44 @@ int64 CWalletTx::GetTxTime() const return nTimeReceived; } +int CWalletTx::GetRequestCount() const +{ + // Returns -1 if it wasn't being tracked + int nRequests = -1; + CRITICAL_BLOCK(cs_mapRequestCount) + { + if (IsCoinBase()) + { + // Generated block + if (hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + } + } + else + { + // Did anyone request this transaction? + map::iterator mi = mapRequestCount.find(GetHash()); + if (mi != mapRequestCount.end()) + { + nRequests = (*mi).second; + // How about the block it's in? + if (nRequests == 0 && hashBlock != 0) + { + map::iterator mi = mapRequestCount.find(hashBlock); + if (mi != mapRequestCount.end()) + nRequests = (*mi).second; + else + nRequests = 1; // If it's in someone else's block it must have got out + } + } + } + } + return nRequests; +} @@ -295,7 +335,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) return 0; - if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, true)) + if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) return 0; pblock = &blockTmp; } @@ -1003,7 +1043,7 @@ bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) foreach(CBlockIndex* pindex, vDisconnect) { CBlock block; - if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos)) return error("Reorganize() : ReadFromDisk for disconnect failed"); if (!block.DisconnectBlock(txdb, pindex)) return error("Reorganize() : DisconnectBlock failed"); @@ -1020,7 +1060,7 @@ bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { CBlockIndex* pindex = vConnect[i]; CBlock block; - if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) + if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos)) return error("Reorganize() : ReadFromDisk for connect failed"); if (!block.ConnectBlock(txdb, pindex)) { @@ -1380,7 +1420,7 @@ bool CheckDiskSpace(int64 nAdditionalBytes) { fShutdown = true; ThreadSafeMessageBox("Warning: Your disk space is low ", "Bitcoin", wxOK | wxICON_EXCLAMATION); - _beginthread(Shutdown, 0, NULL); + CreateThread(Shutdown, NULL); return false; } return true; @@ -1547,7 +1587,7 @@ void PrintBlockTree() // print item CBlock block; - block.ReadFromDisk(pindex, true); + block.ReadFromDisk(pindex); printf("%d (%u,%u) %s %s tx %d", pindex->nHeight, pindex->nFile, @@ -1623,7 +1663,8 @@ bool ProcessMessages(CNode* pfrom) CDataStream& vRecv = pfrom->vRecv; if (vRecv.empty()) return true; - //printf("ProcessMessages(%d bytes)\n", vRecv.size()); + //if (fDebug) + // printf("ProcessMessages(%d bytes)\n", vRecv.size()); // // Message format @@ -1666,7 +1707,8 @@ bool ProcessMessages(CNode* pfrom) { // Rewind and wait for rest of message ///// need a mechanism to give up waiting for overlong message size error - //printf("message-break\n"); + //if (fDebug) + // printf("message-break\n"); vRecv.insert(vRecv.begin(), BEGIN(hdr), END(hdr)); Sleep(100); break; @@ -1718,6 +1760,8 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map > mapReuseKey; RandAddSeedPerfmon(); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("received: %s (%d bytes)\n", strCommand.c_str(), vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { @@ -1739,18 +1783,19 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CAddress addrMe; CAddress addrFrom; uint64 nNonce = 1; + string strSubVer; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; if (pfrom->nVersion >= 106 && !vRecv.empty()) vRecv >> addrFrom >> nNonce; + if (pfrom->nVersion >= 106 && !vRecv.empty()) + vRecv >> strSubVer; if (pfrom->nVersion == 0) return false; // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce) + if (nNonce == nLocalHostNonce && nNonce > 1) { pfrom->fDisconnect = true; - pfrom->vRecv.clear(); - pfrom->vSend.clear(); return true; } @@ -1776,10 +1821,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - // Update the last seen time - if (pfrom->fNetworkNode) - AddressCurrentlyConnected(pfrom->addr); - printf("version message: version %d\n", pfrom->nVersion); } @@ -1824,10 +1865,6 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; - // Update the last seen time for this node's address - if (pfrom->fNetworkNode) - AddressCurrentlyConnected(pfrom->addr); - CTxDB txdb("r"); foreach(const CInv& inv, vInv) { @@ -1842,6 +1879,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AskFor(inv); else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), GetOrphanRoot(mapOrphanBlocks[inv.hash])); + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } } } @@ -1879,6 +1924,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage(inv.GetCommand(), (*mi).second); } } + + // Track requests for our stuff + CRITICAL_BLOCK(cs_mapRequestCount) + { + map::iterator mi = mapRequestCount.find(inv.hash); + if (mi != mapRequestCount.end()) + (*mi).second++; + } } } @@ -2086,11 +2139,23 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "ping") + { + } + + else { // Ignore unknown commands for extensibility } + + // Update the last seen time for this node's address + if (pfrom->fNetworkNode) + if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") + AddressCurrentlyConnected(pfrom->addr); + + return true; } @@ -2129,6 +2194,10 @@ bool SendMessages(CNode* pto) } } + // Keep-alive ping + if (pto->nLastSend && GetTime() - pto->nLastSend > 12 * 60 && pto->vSend.empty()) + pto->PushMessage("ping"); + // // Message: addr @@ -2139,7 +2208,14 @@ bool SendMessages(CNode* pto) { // returns true if wasn't already contained in the set if (pto->setAddrKnown.insert(addr).second) + { vAddrToSend.push_back(addr); + if (vAddrToSend.size() >= 1000) + { + pto->PushMessage("addr", vAddrToSend); + vAddrToSend.clear(); + } + } } pto->vAddrToSend.clear(); if (!vAddrToSend.empty()) @@ -2157,7 +2233,14 @@ bool SendMessages(CNode* pto) { // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) + { vInventoryToSend.push_back(inv); + if (vInventoryToSend.size() >= 1000) + { + pto->PushMessage("inv", vInventoryToSend); + vInventoryToSend.clear(); + } + } } pto->vInventoryToSend.clear(); pto->setInventoryKnown2.clear(); @@ -2179,6 +2262,11 @@ bool SendMessages(CNode* pto) { printf("sending getdata: %s\n", inv.ToString().c_str()); vAskFor.push_back(inv); + if (vAskFor.size() >= 1000) + { + pto->PushMessage("getdata", vAskFor); + vAskFor.clear(); + } } pto->mapAskFor.erase(pto->mapAskFor.begin()); } @@ -2226,8 +2314,8 @@ void GenerateBitcoins(bool fGenerate) int nAddThreads = nProcessors - vnThreadsRunning[3]; printf("Starting %d BitcoinMiner threads\n", nAddThreads); for (int i = 0; i < nAddThreads; i++) - if (_beginthread(ThreadBitcoinMiner, 0, NULL) == -1) - printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); + if (!CreateThread(ThreadBitcoinMiner, NULL)) + printf("Error: CreateThread(ThreadBitcoinMiner) failed\n"); } } @@ -2304,7 +2392,7 @@ void BitcoinMiner() CBigNum bnExtraNonce = 0; while (fGenerateBitcoins) { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(50); if (fShutdown) return; @@ -2440,7 +2528,7 @@ void BitcoinMiner() printf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + SetThreadPriority(THREAD_PRIORITY_NORMAL); CRITICAL_BLOCK(cs_main) { if (pindexPrev == pindexBest) @@ -2450,12 +2538,16 @@ void BitcoinMiner() return; key.MakeNewKey(); + // Track how many getdata requests this block gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[pblock->GetHash()] = 0; + // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock.release())) printf("ERROR in BitcoinMiner, ProcessBlock, block not accepted\n"); } } - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); + SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(500); break; @@ -2534,7 +2626,7 @@ bool SelectCoins(int64 nTargetValue, set& setCoinsRet) setCoinsRet.clear(); // List of values less than target - int64 nLowestLarger = _I64_MAX; + int64 nLowestLarger = INT64_MAX; CWalletTx* pcoinLowestLarger = NULL; vector > vValue; int64 nTotalLower = 0; @@ -2777,6 +2869,10 @@ bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew) return error("SendMoney() : Error finalizing transaction"); } + // Track how many getdata requests our transaction gets + CRITICAL_BLOCK(cs_mapRequestCount) + mapRequestCount[wtxNew.GetHash()] = 0; + printf("SendMoney: %s\n", wtxNew.GetHash().ToString().substr(0,6).c_str()); // Broadcast diff --git a/main.h b/main.h index 853fdfa5b..8bb1e19e8 100644 --- a/main.h +++ b/main.h @@ -34,6 +34,8 @@ extern int nBestHeight; extern uint256 hashBestChain; extern CBlockIndex* pindexBest; extern unsigned int nTransactionsUpdated; +extern map mapRequestCount; +extern CCriticalSection cs_mapRequestCount; // Settings extern int fGenerateBitcoins; @@ -647,6 +649,15 @@ public: nGetCreditCached = 0; } + IMPLEMENT_SERIALIZE + ( + nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); + nVersion = this->nVersion; + READWRITE(hashBlock); + READWRITE(vMerkleBranch); + READWRITE(nIndex); + ) + int64 GetCredit(bool fUseCache=false) const { // Must wait until coinbase is safely deep enough in the chain before valuing it @@ -661,15 +672,6 @@ public: return nGetCreditCached; } - IMPLEMENT_SERIALIZE - ( - nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); - nVersion = this->nVersion; - READWRITE(hashBlock); - READWRITE(vMerkleBranch); - READWRITE(nIndex); - ) - int SetMerkleBranch(const CBlock* pblock=NULL); int GetDepthInMainChain() const; @@ -749,6 +751,7 @@ public: int64 GetTxTime() const; + int GetRequestCount() const; void AddSupportingTransactions(CTxDB& txdb); @@ -978,7 +981,7 @@ public: return true; } - bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions) + bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) { SetNull(); @@ -1027,7 +1030,7 @@ public: int64 GetBlockValue(int64 nFees) const; bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions); + bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions=true); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); bool CheckBlock() const; bool AcceptBlock(); diff --git a/net.cpp b/net.cpp index 0569604ba..71295d5de 100644 --- a/net.cpp +++ b/net.cpp @@ -13,7 +13,6 @@ bool OpenNetworkConnection(const CAddress& addrConnect); - // // Global state variables // @@ -25,6 +24,7 @@ uint64 nLocalHostNonce = 0; bool fShutdown = false; array vnThreadsRunning; SOCKET hListenSocket = INVALID_SOCKET; +int64 nThreadSocketHandlerHeartbeat = INT64_MAX; vector vNodes; CCriticalSection cs_vNodes; @@ -65,7 +65,7 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) if (fProxy) { - printf("Proxy connecting %s\n", addrConnect.ToStringLog().c_str()); + printf("proxy connecting %s\n", addrConnect.ToStringLog().c_str()); char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user"; memcpy(pszSocks4IP + 2, &addrConnect.port, 2); memcpy(pszSocks4IP + 4, &addrConnect.ip, 4); @@ -87,9 +87,11 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) if (pchRet[1] != 0x5a) { closesocket(hSocket); - return error("Proxy returned error %d", pchRet[1]); + if (pchRet[1] != 0x5b) + printf("ERROR: Proxy returned error %d\n", pchRet[1]); + return false; } - printf("Proxy connection established %s\n", addrConnect.ToStringLog().c_str()); + printf("proxy connected %s\n", addrConnect.ToStringLog().c_str()); } hSocketRet = hSocket; @@ -219,6 +221,7 @@ bool AddAddress(CAddrDB& addrdb, CAddress addr, bool fCurrentlyOnline) if (it == mapAddresses.end()) { // New address + printf("AddAddress(%s)\n", addr.ToStringLog().c_str()); mapAddresses.insert(make_pair(addr.GetKey(), addr)); addrdb.WriteAddress(addr); return true; @@ -256,7 +259,7 @@ void AddressCurrentlyConnected(const CAddress& addr) if (it != mapAddresses.end()) { CAddress& addrFound = (*it).second; - int64 nUpdateInterval = 60 * 60; + int64 nUpdateInterval = 20 * 60; if (addrFound.nTime < GetAdjustedTime() - nUpdateInterval) { // Periodically update most recently seen time @@ -417,7 +420,13 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) } /// debug print - printf("trying connection %s\n", addrConnect.ToStringLog().c_str()); + printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n", + addrConnect.ToStringLog().c_str(), + (double)(addrConnect.nTime - GetAdjustedTime())/3600.0, + (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0); + + CRITICAL_BLOCK(cs_mapAddresses) + mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime(); // Connect SOCKET hSocket; @@ -428,7 +437,7 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) // Set to nonblocking #ifdef __WXMSW__ - u_long nOne = 1; + u_long nOne = 1; if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) printf("ConnectSocket() : ioctlsocket nonblocking setting failed, error %d\n", WSAGetLastError()); #else @@ -445,29 +454,23 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) CRITICAL_BLOCK(cs_vNodes) vNodes.push_back(pnode); - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastFailed = 0; + pnode->nTimeConnected = GetTime(); return pnode; } else { - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addrConnect.GetKey()].nLastFailed = GetAdjustedTime(); return NULL; } } void CNode::DoDisconnect() { + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("disconnecting node %s\n", addr.ToStringLog().c_str()); closesocket(hSocket); - // If outbound and never got version message, mark address as failed - if (!fInbound && !fSuccessfullyConnected) - CRITICAL_BLOCK(cs_mapAddresses) - mapAddresses[addr.GetKey()].nLastFailed = GetAdjustedTime(); - // All of a nodes broadcasts and subscriptions are automatically torn down // when it goes down, so a node has to stay up to keep its broadcast going. @@ -508,7 +511,7 @@ void ThreadSocketHandler(void* parg) PrintException(&e, "ThreadSocketHandler()"); } catch (...) { vnThreadsRunning[0]--; - PrintException(NULL, "ThreadSocketHandler()"); + throw; // support pthread_cancel() } printf("ThreadSocketHandler exiting\n"); @@ -531,15 +534,18 @@ void ThreadSocketHandler2(void* parg) vector vNodesCopy = vNodes; foreach(CNode* pnode, vNodesCopy) { - if (pnode->ReadyToDisconnect() && pnode->vRecv.empty() && pnode->vSend.empty()) + if (pnode->fDisconnect || + (pnode->GetRefCount() <= 0 && pnode->vRecv.empty() && pnode->vSend.empty())) { // remove from vNodes vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); + + // close socket pnode->DoDisconnect(); // hold in disconnected pool until all refs are released pnode->nReleaseTime = max(pnode->nReleaseTime, GetTime() + 5 * 60); - if (pnode->fNetworkNode) + if (pnode->fNetworkNode || pnode->fInbound) pnode->Release(); vNodesDisconnected.push_back(pnode); } @@ -582,8 +588,10 @@ void ThreadSocketHandler2(void* parg) fd_set fdsetRecv; fd_set fdsetSend; + fd_set fdsetError; FD_ZERO(&fdsetRecv); FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); SOCKET hSocketMax = 0; FD_SET(hListenSocket, &fdsetRecv); hSocketMax = max(hSocketMax, hListenSocket); @@ -592,6 +600,7 @@ void ThreadSocketHandler2(void* parg) foreach(CNode* pnode, vNodes) { FD_SET(pnode->hSocket, &fdsetRecv); + FD_SET(pnode->hSocket, &fdsetError); hSocketMax = max(hSocketMax, pnode->hSocket); TRY_CRITICAL_BLOCK(pnode->cs_vSend) if (!pnode->vSend.empty()) @@ -600,30 +609,21 @@ void ThreadSocketHandler2(void* parg) } vnThreadsRunning[0]--; - int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); + int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); vnThreadsRunning[0]++; if (fShutdown) return; if (nSelect == SOCKET_ERROR) { int nErr = WSAGetLastError(); - printf("select failed: %d\n", nErr); + printf("socket select error %d\n", nErr); for (int i = 0; i <= hSocketMax; i++) - { FD_SET(i, &fdsetRecv); - FD_SET(i, &fdsetSend); - } + FD_ZERO(&fdsetSend); + FD_ZERO(&fdsetError); Sleep(timeout.tv_usec/1000); } - //// debug print - //foreach(CNode* pnode, vNodes) - //{ - // printf("vRecv = %-5d ", pnode->vRecv.size()); - // printf("vSend = %-5d ", pnode->vSend.size()); - //} - //printf("\n"); - // // Accept new connections @@ -641,7 +641,7 @@ void ThreadSocketHandler2(void* parg) if (hSocket == INVALID_SOCKET) { if (WSAGetLastError() != WSAEWOULDBLOCK) - printf("ERROR ThreadSocketHandler accept failed: %d\n", WSAGetLastError()); + printf("socket error accept failed: %d\n", WSAGetLastError()); } else { @@ -669,7 +669,7 @@ void ThreadSocketHandler2(void* parg) // // Receive // - if (FD_ISSET(hSocket, &fdsetRecv)) + if (FD_ISSET(hSocket, &fdsetRecv) || FD_ISSET(hSocket, &fdsetError)) { TRY_CRITICAL_BLOCK(pnode->cs_vRecv) { @@ -677,25 +677,29 @@ void ThreadSocketHandler2(void* parg) unsigned int nPos = vRecv.size(); // typical socket buffer is 8K-64K - const unsigned int nBufSize = 0x10000; - vRecv.resize(nPos + nBufSize); - int nBytes = recv(hSocket, &vRecv[nPos], nBufSize, 0); - vRecv.resize(nPos + max(nBytes, 0)); - if (nBytes == 0) + char pchBuf[0x10000]; + int nBytes = recv(hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + if (nBytes > 0) + { + vRecv.resize(nPos + nBytes); + memcpy(&vRecv[nPos], pchBuf, nBytes); + pnode->nLastRecv = GetTime(); + } + else if (nBytes == 0) { // socket closed gracefully if (!pnode->fDisconnect) - printf("recv: socket closed\n"); + printf("socket closed\n"); pnode->fDisconnect = true; } else if (nBytes < 0) { - // socket error + // error int nErr = WSAGetLastError(); if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) { if (!pnode->fDisconnect) - printf("recv failed: %d\n", nErr); + printf("socket recv error %d\n", nErr); pnode->fDisconnect = true; } } @@ -712,28 +716,63 @@ void ThreadSocketHandler2(void* parg) CDataStream& vSend = pnode->vSend; if (!vSend.empty()) { - int nBytes = send(hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL); + int nBytes = send(hSocket, &vSend[0], vSend.size(), MSG_NOSIGNAL | MSG_DONTWAIT); if (nBytes > 0) { vSend.erase(vSend.begin(), vSend.begin() + nBytes); + pnode->nLastSend = GetTime(); } - else if (nBytes == 0) - { - if (pnode->ReadyToDisconnect()) - pnode->vSend.clear(); - } - else + else if (nBytes < 0) { - printf("send error %d\n", nBytes); - if (pnode->ReadyToDisconnect()) - pnode->vSend.clear(); + // error + int nErr = WSAGetLastError(); + if (nErr != WSAEWOULDBLOCK && nErr != WSAEMSGSIZE && nErr != WSAEINTR && nErr != WSAEINPROGRESS) + { + printf("socket send error %d\n", nErr); + pnode->fDisconnect = true; + } } } } } + + // + // Inactivity checking + // + if (pnode->vSend.empty()) + pnode->nLastSendEmpty = GetTime(); + if (GetTime() - pnode->nTimeConnected > 60) + { + if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) + { + printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastSend > 10 * 60 && GetTime() - pnode->nLastSendEmpty > 10 * 60) + { + printf("socket not sending\n"); + pnode->fDisconnect = true; + } + else if (GetTime() - pnode->nLastRecv > (pnode->nVersion >= 107 ? 15*60 : 90*60)) + { + printf("socket inactivity timeout\n"); + pnode->fDisconnect = true; + } + } + } + + + //// debug heartbeat + static int64 nHeartbeat1; + if (GetTime() - nHeartbeat1 >= 5 * 60) + { + printf("%s sendrecv\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); + nHeartbeat1 = GetTime(); + fDebug = true; } + nThreadSocketHandlerHeartbeat = GetTime(); Sleep(10); } } @@ -772,15 +811,20 @@ void ThreadOpenConnections2(void* parg) { printf("ThreadOpenConnections started\n"); - // Connect to one specified address + // Connect to specific addresses while (mapArgs.count("-connect")) { - OpenNetworkConnection(CAddress(mapArgs["-connect"])); - for (int i = 0; i < 10; i++) + foreach(string strAddr, mapMultiArgs["-connect"]) { - Sleep(1000); - if (fShutdown) - return; + CAddress addr(strAddr, NODE_NETWORK); + if (addr.IsValid()) + OpenNetworkConnection(addr); + for (int i = 0; i < 10; i++) + { + Sleep(1000); + if (fShutdown) + return; + } } } @@ -821,12 +865,7 @@ void ThreadOpenConnections2(void* parg) // Choose an address to connect to based on most recently seen // CAddress addrConnect; - int64 nBestTime = 0; - int64 nDelay = ((60 * 60) << vNodes.size()); - if (vNodes.size() >= 3) - nDelay *= 4; - if (nGotIRCAddresses > 0) - nDelay *= 100; + int64 nBest = INT64_MIN; // Do this here so we don't have to critsect vNodes inside mapAddresses critsect set setConnected; @@ -841,24 +880,51 @@ void ThreadOpenConnections2(void* parg) const CAddress& addr = item.second; if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.ip)) continue; + int64 nSinceLastSeen = GetAdjustedTime() - addr.nTime; + int64 nSinceLastTry = GetAdjustedTime() - addr.nLastTry; // Randomize the order in a deterministic way, putting the standard port first - int64 nRandomizer = (uint64)(addr.nLastFailed * 9567851 + addr.ip * 7789) % (1 * 60 * 60); + int64 nRandomizer = (uint64)(addr.nLastTry * 9567851 + addr.ip * 7789) % (30 * 60); if (addr.port != DEFAULT_PORT) - nRandomizer += 1 * 60 * 60; + nRandomizer += 30 * 60; + + // Last seen Base retry frequency + // <1 hour 10 min + // 1 hour 1 hour + // 4 hours 2 hours + // 24 hours 5 hours + // 48 hours 7 hours + // 7 days 13 hours + // 30 days 27 hours + // 90 days 46 hours + // 365 days 93 hours + int64 nDelay = 3600.0 * sqrt(fabs(nSinceLastSeen) / 3600.0) + nRandomizer; + + // Fast reconnect for one hour after last seen + if (nSinceLastSeen < 60 * 60) + nDelay = 10 * 60; // Limit retry frequency - if (GetAdjustedTime() < addr.nLastFailed + nDelay + nRandomizer) + if (nSinceLastTry < nDelay) continue; - // Try again only after all addresses had a first attempt - int64 nTime = addr.nTime - nRandomizer; - if (addr.nLastFailed > addr.nTime) - nTime -= 365 * 24 * 60 * 60; + // If we have IRC, we'll be notified when they first come online, + // and again every 24 hours by the refresh broadcast. + if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60) + continue; - if (nTime > nBestTime) + // Only try the old stuff if we don't have enough connections + if (vNodes.size() >= 2 && nSinceLastSeen > 7 * 24 * 60 * 60) + continue; + if (vNodes.size() >= 4 && nSinceLastSeen > 24 * 60 * 60) + continue; + + // If multiple addresses are ready, prioritize by time since + // last seen and time since last tried. + int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer; + if (nScore > nBest) { - nBestTime = nTime; + nBest = nScore; addrConnect = addr; } } @@ -941,7 +1007,7 @@ void ThreadMessageHandler(void* parg) void ThreadMessageHandler2(void* parg) { printf("ThreadMessageHandler started\n"); - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); loop { // Poll the connected nodes for messages @@ -1063,39 +1129,31 @@ bool BindListenPort(string& strError) return true; } -bool StartNode(string& strError) +void StartNode(void* parg) { - strError = ""; if (pnodeLocalHost == NULL) pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); #ifdef __WXMSW__ // Get local host ip - char pszHostName[255]; - if (gethostname(pszHostName, sizeof(pszHostName)) == SOCKET_ERROR) + char pszHostName[1000] = ""; + if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { - strError = strprintf("Error: Unable to get IP address of this computer (gethostname returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - struct hostent* phostent = gethostbyname(pszHostName); - if (!phostent) - { - strError = strprintf("Error: Unable to get IP address of this computer (gethostbyname returned error %d)", WSAGetLastError()); - printf("%s\n", strError.c_str()); - return false; - } - - // Take the first IP that isn't loopback 127.x.x.x - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); - for (int i = 0; phostent->h_addr_list[i] != NULL; i++) - { - CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); - if (addr.IsValid() && addr.GetByte(3) != 127) + struct hostent* phostent = gethostbyname(pszHostName); + if (phostent) { - addrLocalHost = addr; - break; + // Take the first IP that isn't loopback 127.x.x.x + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + printf("host ip %d: %s\n", i, CAddress(*(unsigned int*)phostent->h_addr_list[i]).ToStringIP().c_str()); + for (int i = 0; phostent->h_addr_list[i] != NULL; i++) + { + CAddress addr(*(unsigned int*)phostent->h_addr_list[i], DEFAULT_PORT, nLocalServices); + if (addr.IsValid() && addr.GetByte(3) != 127) + { + addrLocalHost = addr; + break; + } + } } } #else @@ -1145,45 +1203,85 @@ bool StartNode(string& strError) } else { - if (addrIncoming.ip) + if (addrIncoming.IsValid()) addrLocalHost.ip = addrIncoming.ip; if (GetMyExternalIP(addrLocalHost.ip)) { addrIncoming = addrLocalHost; CWalletDB().WriteSetting("addrIncoming", addrIncoming); + printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); } } - // Get addresses from IRC and advertise ours - if (_beginthread(ThreadIRCSeed, 0, NULL) == -1) - printf("Error: _beginthread(ThreadIRCSeed) failed\n"); - // // Start threads // - if (_beginthread(ThreadSocketHandler, 0, NULL) == -1) - { - strError = "Error: _beginthread(ThreadSocketHandler) failed"; - printf("%s\n", strError.c_str()); - return false; - } - if (_beginthread(ThreadOpenConnections, 0, NULL) == -1) - { - strError = "Error: _beginthread(ThreadOpenConnections) failed"; - printf("%s\n", strError.c_str()); - return false; - } + // Get addresses from IRC and advertise ours + if (!CreateThread(ThreadIRCSeed, NULL)) + printf("Error: CreateThread(ThreadIRCSeed) failed\n"); + + // Send and receive from sockets, accept connections + pthread_t hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + + // Initiate outbound connections + if (!CreateThread(ThreadOpenConnections, NULL)) + printf("Error: CreateThread(ThreadOpenConnections) failed\n"); + + // Process messages + if (!CreateThread(ThreadMessageHandler, NULL)) + printf("Error: CreateThread(ThreadMessageHandler) failed\n"); - if (_beginthread(ThreadMessageHandler, 0, NULL) == -1) + // Generate coins in the background + GenerateBitcoins(fGenerateBitcoins); + + // + // Thread monitoring + // + loop { - strError = "Error: _beginthread(ThreadMessageHandler) failed"; - printf("%s\n", strError.c_str()); - return false; + Sleep(15000); + if (GetTime() - nThreadSocketHandlerHeartbeat > 4 * 60) + { + // First see if closing sockets will free it + printf("*** ThreadSocketHandler is stopped ***\n"); + CRITICAL_BLOCK(cs_vNodes) + { + foreach(CNode* pnode, vNodes) + { + bool fGot = false; + TRY_CRITICAL_BLOCK(pnode->cs_vRecv) + TRY_CRITICAL_BLOCK(pnode->cs_vSend) + fGot = true; + if (!fGot) + { + printf("*** closing socket\n"); + closesocket(pnode->hSocket); + pnode->fDisconnect = true; + } + } + } + Sleep(10000); + if (GetTime() - nThreadSocketHandlerHeartbeat < 60) + continue; + + // Hopefully it never comes to this. + // We know it'll always be hung in the recv or send call. + // cs_vRecv or cs_vSend may be left permanently unreleased, + // but we always only use TRY_CRITICAL_SECTION on them. + printf("*** Restarting ThreadSocketHandler ***\n"); + TerminateThread(hThreadSocketHandler, 0); + #ifdef __WXMSW__ + CloseHandle(hThreadSocketHandler); + #endif + vnThreadsRunning[0] = 0; + + // Restart + hThreadSocketHandler = CreateThread(ThreadSocketHandler, NULL, true); + nThreadSocketHandlerHeartbeat = GetTime(); + } } - - return true; } bool StopNode() diff --git a/net.h b/net.h index 7b83d462f..6300d3efc 100644 --- a/net.h +++ b/net.h @@ -29,7 +29,7 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); bool AnySubscribed(unsigned int nChannel); bool BindListenPort(string& strError=REF(string())); -bool StartNode(string& strError=REF(string())); +void StartNode(void* parg); bool StopNode(); @@ -39,7 +39,6 @@ bool StopNode(); - // // Message header // (4) message start @@ -139,7 +138,7 @@ public: unsigned int nTime; // memory only - unsigned int nLastFailed; + unsigned int nLastTry; CAddress() { @@ -183,7 +182,7 @@ public: ip = INADDR_NONE; port = DEFAULT_PORT; nTime = GetAdjustedTime(); - nLastFailed = 0; + nLastTry = 0; } bool SetAddress(const char* pszIn) @@ -458,6 +457,7 @@ extern uint64 nLocalHostNonce; extern bool fShutdown; extern array vnThreadsRunning; extern SOCKET hListenSocket; +extern int64 nThreadSocketHandlerHeartbeat; extern vector vNodes; extern CCriticalSection cs_vNodes; @@ -486,6 +486,10 @@ public: CDataStream vRecv; CCriticalSection cs_vSend; CCriticalSection cs_vRecv; + int64 nLastSend; + int64 nLastRecv; + int64 nLastSendEmpty; + int64 nTimeConnected; unsigned int nPushPos; CAddress addr; int nVersion; @@ -523,6 +527,10 @@ public: hSocket = hSocketIn; vSend.SetType(SER_NETWORK); vRecv.SetType(SER_NETWORK); + nLastSend = 0; + nLastRecv = 0; + nLastSendEmpty = GetTime(); + nTimeConnected = GetTime(); nPushPos = -1; addr = addrIn; nVersion = 0; @@ -542,7 +550,7 @@ public: CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); CAddress addrMe = (fUseProxy ? CAddress("0.0.0.0") : addrLocalHost); RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); - PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce); + PushMessage("version", VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, "linux-test5"); } ~CNode() @@ -557,11 +565,6 @@ private: public: - bool ReadyToDisconnect() - { - return fDisconnect || GetRefCount() <= 0; - } - int GetRefCount() { return max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); @@ -635,6 +638,8 @@ public: AbortMessage(); nPushPos = vSend.size(); vSend << CMessageHeader(pszCommand, 0); + if (fDebug) + printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); printf("sending: %s ", pszCommand); } diff --git a/ui.cpp b/ui.cpp index e12970b81..9d7556cb5 100644 --- a/ui.cpp +++ b/ui.cpp @@ -25,6 +25,7 @@ map mapAddressBook; bool fRandSendTest = false; void RandSend(); extern int g_isPainting; +bool fClosedToTray = false; // Settings int fShowGenerated = true; @@ -413,16 +414,17 @@ void Shutdown(void* parg) void CMainFrame::OnClose(wxCloseEvent& event) { - if (fMinimizeToTray && fMinimizeOnClose && event.CanVeto() && !IsIconized()) + if (fMinimizeOnClose && event.CanVeto() && !IsIconized()) { // Divert close to minimize event.Veto(); + fClosedToTray = true; Iconize(true); } else { Destroy(); - _beginthread(Shutdown, 0, NULL); + CreateThread(Shutdown, NULL); } } @@ -430,7 +432,16 @@ void CMainFrame::OnIconize(wxIconizeEvent& event) { // Hide the task bar button when minimized. // Event is sent when the frame is minimized or restored. - Show(!fMinimizeToTray || !event.Iconized()); + if (!event.Iconized()) + fClosedToTray = false; +#ifndef __WXMSW__ + // Tray is not reliable on Linux gnome + fClosedToTray = false; +#endif + if (fMinimizeToTray && event.Iconized()) + fClosedToTray = true; + Show(!fClosedToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); } void CMainFrame::OnMouseEvents(wxMouseEvent& event) @@ -527,7 +538,6 @@ bool CMainFrame::DeleteLine(uint256 hashKey) string FormatTxStatus(const CWalletTx& wtx) { // Status - int nDepth = wtx.GetDepthInMainChain(); if (!wtx.IsFinal()) { if (wtx.nLockTime < 500000000) @@ -535,10 +545,16 @@ string FormatTxStatus(const CWalletTx& wtx) else return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str()); } - else if (nDepth < 6) - return strprintf("%d/unconfirmed", nDepth); else - return strprintf("%d blocks", nDepth); + { + int nDepth = wtx.GetDepthInMainChain(); + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + return strprintf("%d/offline?", nDepth); + else if (nDepth < 6) + return strprintf("%d/unconfirmed", nDepth); + else + return strprintf("%d blocks", nDepth); + } } string SingleLine(const string& strIn) @@ -629,9 +645,17 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) foreach(const CTxOut& txout, wtx.vout) nUnmatured += txout.GetCredit(); if (wtx.IsInMainChain()) - strDescription += strprintf(" (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + { + strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity()); + + // Check if the block was requested by anyone + if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!"; + } else - strDescription += " (not accepted)"; + { + strDescription = "Generated (not accepted)"; + } } } else if (!mapValue["from"].empty() || !mapValue["message"].empty()) @@ -701,8 +725,11 @@ bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex) strStatus, nTime ? DateTimeStr(nTime) : "", "Payment to yourself", - FormatMoney(nNet - nValue, true), - FormatMoney(nValue, true)); + "", + ""); + /// issue: can't tell which is the payment and which is the change anymore + // FormatMoney(nNet - nValue, true), + // FormatMoney(nValue, true)); } else if (fAllFromMe) { @@ -1028,6 +1055,9 @@ void CMainFrame::OnPaintListCtrl(wxPaintEvent& event) string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount); m_statusBar->SetStatusText(strStatus, 2); + if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60) + m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0); + // Pass through to listctrl to actually do the paint, we're just hooking the message m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this); m_listCtrl->GetEventHandler()->ProcessEvent(event); @@ -1237,7 +1267,19 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails - strHTML += "Status: " + FormatTxStatus(wtx) + "
"; + strHTML += "Status: " + FormatTxStatus(wtx); + int nRequests = wtx.GetRequestCount(); + if (nRequests != -1) + { + if (nRequests == 0) + strHTML += ", has not been successfully broadcast yet"; + else if (nRequests == 1) + strHTML += strprintf(", broadcast through %d node", nRequests); + else + strHTML += strprintf(", broadcast through %d nodes", nRequests); + } + strHTML += "
"; + strHTML += "Date: " + (nTime ? DateTimeStr(nTime) : "") + "
"; @@ -1366,9 +1408,10 @@ CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetails if (fAllToMe) { // Payment to self - int64 nValue = wtx.vout[0].nValue; - strHTML += "Debit: " + FormatMoney(-nValue) + "
"; - strHTML += "Credit: " + FormatMoney(nValue) + "
"; + /// issue: can't tell which is the payment and which is the change anymore + //int64 nValue = wtx.vout[0].nValue; + //strHTML += "Debit: " + FormatMoney(-nValue) + "
"; + //strHTML += "Credit: " + FormatMoney(nValue) + "
"; } int64 nTxFee = nDebit - wtx.GetValueOut(); @@ -1469,6 +1512,9 @@ COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent) //m_listBox->Append("Test 2"); m_listBox->SetSelection(0); SelectPage(0); +#ifndef __WXMSW__ + m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close"); +#endif // Init values m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee)); @@ -1481,9 +1527,7 @@ COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent) m_spinCtrlLimitProcessors->SetRange(1, nProcessors); m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup()); m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray); - m_checkBoxMinimizeOnClose->Enable(fMinimizeToTray); - m_checkBoxMinimizeOnClose->SetValue(fMinimizeToTray && fMinimizeOnClose); - fTmpMinimizeOnClose = fMinimizeOnClose; + m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose); m_checkBoxUseProxy->SetValue(fUseProxy); m_textCtrlProxyIP->Enable(fUseProxy); m_textCtrlProxyPort->Enable(fUseProxy); @@ -1521,22 +1565,6 @@ void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event) m_spinCtrlLimitProcessors->Enable(event.IsChecked()); } -void COptionsDialog::OnCheckBoxMinimizeToTray(wxCommandEvent& event) -{ - m_checkBoxMinimizeOnClose->Enable(event.IsChecked()); - - // Save the value in fTmpMinimizeOnClose so we can - // show the checkbox unchecked when its parent is unchecked - if (event.IsChecked()) - m_checkBoxMinimizeOnClose->SetValue(fTmpMinimizeOnClose); - else - { - fTmpMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue(); - m_checkBoxMinimizeOnClose->SetValue(false); - } - -} - void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event) { m_textCtrlProxyIP->Enable(event.IsChecked()); @@ -1608,12 +1636,12 @@ void COptionsDialog::OnButtonApply(wxCommandEvent& event) { fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue(); walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray); - ptaskbaricon->Show(fMinimizeToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); } - if (fMinimizeOnClose != (fMinimizeToTray ? m_checkBoxMinimizeOnClose->GetValue() : fTmpMinimizeOnClose)) + if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue()) { - fMinimizeOnClose = (fMinimizeToTray ? m_checkBoxMinimizeOnClose->GetValue() : fTmpMinimizeOnClose); + fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue(); walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose); } @@ -1643,6 +1671,9 @@ CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent) if (str.Find('Â') != wxNOT_FOUND) str.Remove(str.Find('Â'), 1); m_staticTextMain->SetLabel(str); +#ifndef __WXMSW__ + SetSize(510, 380); +#endif } void CAboutDialog::OnButtonOK(wxCommandEvent& event) @@ -1849,7 +1880,7 @@ CSendingDialog::CSendingDialog(wxWindow* parent, const CAddress& addrIn, int64 n SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str())); m_textCtrlStatus->SetValue(""); - _beginthread(SendingDialogStartTransfer, 0, this); + CreateThread(SendingDialogStartTransfer, this); } CSendingDialog::~CSendingDialog() @@ -2856,7 +2887,7 @@ CViewProductDialog::CViewProductDialog(wxWindow* parent, const CProduct& product this->Layout(); // Request details from seller - _beginthread(ThreadRequestProductDetails, 0, new pair(product, GetEventHandler())); + CreateThread(ThreadRequestProductDetails, new pair(product, GetEventHandler())); } CViewProductDialog::~CViewProductDialog() @@ -3256,6 +3287,7 @@ void CEditReviewDialog::GetReview(CReview& review) enum { ID_TASKBAR_RESTORE = 10001, + ID_TASKBAR_OPTIONS, ID_TASKBAR_GENERATE, ID_TASKBAR_EXIT, }; @@ -3263,6 +3295,7 @@ enum BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon) EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick) EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore) + EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions) EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate) EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate) EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit) @@ -3312,9 +3345,18 @@ void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event) Restore(); } +void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event) +{ + // Since it's modal, get the main window to do it + wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS); + pframeMain->AddPendingEvent(event2); +} + void CMyTaskBarIcon::Restore() { pframeMain->Show(); + wxIconizeEvent event(0, false); + pframeMain->AddPendingEvent(event); pframeMain->Iconize(false); pframeMain->Raise(); } @@ -3344,6 +3386,7 @@ wxMenu* CMyTaskBarIcon::CreatePopupMenu() { wxMenu* pmenu = new wxMenu; pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin"); + pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions..."); pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins); #ifndef __WXMAC_OSX__ // Mac has built-in quit menu pmenu->AppendSeparator(); @@ -3582,7 +3625,7 @@ bool CMyApp::OnInit2() { CBlockIndex* pindex = (*mi).second; CBlock block; - block.ReadFromDisk(pindex, true); + block.ReadFromDisk(pindex); block.BuildMerkleTree(); block.print(); printf("\n"); @@ -3632,20 +3675,20 @@ bool CMyApp::OnInit2() if (mapArgs.count("-min")) pframeMain->Iconize(true); pframeMain->Show(true); // have to show first to get taskbar button to hide - pframeMain->Show(!fMinimizeToTray || !pframeMain->IsIconized()); - ptaskbaricon->Show(fMinimizeToTray); + if (fMinimizeToTray && pframeMain->IsIconized()) + fClosedToTray = true; + pframeMain->Show(!fClosedToTray); + ptaskbaricon->Show(fMinimizeToTray || fClosedToTray); - _beginthread(ThreadDelayedRepaint, 0, NULL); + CreateThread(ThreadDelayedRepaint, NULL); if (!CheckDiskSpace()) return false; RandAddSeedPerfmon(); - if (!StartNode(strErrors)) - wxMessageBox(strErrors, "Bitcoin"); - - GenerateBitcoins(fGenerateBitcoins); + if (!CreateThread(StartNode, NULL)) + wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); if (fFirstRun) SetStartOnSystemStartup(true); diff --git a/ui.h b/ui.h index a919c3668..c4bf8b66f 100644 --- a/ui.h +++ b/ui.h @@ -112,7 +112,6 @@ protected: void OnListBox(wxCommandEvent& event); void OnKillFocusTransactionFee(wxFocusEvent& event); void OnCheckBoxLimitProcessors(wxCommandEvent& event); - void OnCheckBoxMinimizeToTray(wxCommandEvent& event); void OnCheckBoxUseProxy(wxCommandEvent& event); void OnKillFocusProxy(wxFocusEvent& event); @@ -447,6 +446,7 @@ protected: // Event handlers void OnLeftButtonDClick(wxTaskBarIconEvent& event); void OnMenuRestore(wxCommandEvent& event); + void OnMenuOptions(wxCommandEvent& event); void OnUpdateUIGenerate(wxUpdateUIEvent& event); void OnMenuGenerate(wxCommandEvent& event); void OnMenuExit(wxCommandEvent& event); diff --git a/uibase.cpp b/uibase.cpp index 7bc8081f0..f05f1095f 100644 --- a/uibase.cpp +++ b/uibase.cpp @@ -45,7 +45,7 @@ CMainFrameBase::CMainFrameBase( wxWindow* parent, wxWindowID id, const wxString& m_menuOptions->Append( m_menuOptionsChangeYourAddress ); wxMenuItem* m_menuOptionsOptions; - m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_ANY, wxString( wxT("&Options...") ) , wxEmptyString, wxITEM_NORMAL ); + m_menuOptionsOptions = new wxMenuItem( m_menuOptions, wxID_MENUOPTIONSOPTIONS, wxString( wxT("&Options...") ) , wxEmptyString, wxITEM_NORMAL ); m_menuOptions->Append( m_menuOptionsOptions ); m_menubar->Append( m_menuOptions, wxT("&Options") ); @@ -428,21 +428,13 @@ COptionsDialogBase::COptionsDialogBase( wxWindow* parent, wxWindowID id, const w bSizer69->Add( m_checkBoxStartOnSystemStartup, 0, wxALL, 5 ); - m_checkBoxMinimizeToTray = new wxCheckBox( m_panelMain, wxID_ANY, wxT("&Minimize to the system tray instead of the taskbar"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxMinimizeToTray = new wxCheckBox( m_panelMain, wxID_ANY, wxT("&Minimize to the tray instead of the taskbar"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer69->Add( m_checkBoxMinimizeToTray, 0, wxALL, 5 ); - wxBoxSizer* bSizer101; - bSizer101 = new wxBoxSizer( wxHORIZONTAL ); + m_checkBoxMinimizeOnClose = new wxCheckBox( m_panelMain, wxID_ANY, wxT("M&inimize to the tray on close"), wxDefaultPosition, wxDefaultSize, 0 ); - - bSizer101->Add( 16, 0, 0, 0, 5 ); - - m_checkBoxMinimizeOnClose = new wxCheckBox( m_panelMain, wxID_ANY, wxT("Mi&nimize to system tray on close"), wxDefaultPosition, wxDefaultSize, 0 ); - - bSizer101->Add( m_checkBoxMinimizeOnClose, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - bSizer69->Add( bSizer101, 1, wxEXPAND, 5 ); + bSizer69->Add( m_checkBoxMinimizeOnClose, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); wxBoxSizer* bSizer102; bSizer102 = new wxBoxSizer( wxHORIZONTAL ); diff --git a/uibase.h b/uibase.h index 2cb99e9a5..e0c152185 100644 --- a/uibase.h +++ b/uibase.h @@ -43,51 +43,52 @@ #define wxID_MAINFRAME 1000 #define wxID_VIEWSHOWGENERATED 1001 #define wxID_OPTIONSGENERATEBITCOINS 1002 -#define wxID_BUTTONSEND 1003 -#define wxID_BUTTONRECEIVE 1004 -#define wxID_TEXTCTRLADDRESS 1005 -#define wxID_BUTTONCOPY 1006 -#define wxID_BUTTONCHANGE 1007 -#define wxID_TRANSACTIONFEE 1008 -#define wxID_PROXYIP 1009 -#define wxID_PROXYPORT 1010 -#define wxID_TEXTCTRLPAYTO 1011 -#define wxID_BUTTONPASTE 1012 -#define wxID_BUTTONADDRESSBOOK 1013 -#define wxID_TEXTCTRLAMOUNT 1014 -#define wxID_CHOICETRANSFERTYPE 1015 -#define wxID_LISTCTRL 1016 -#define wxID_BUTTONRENAME 1017 -#define wxID_BUTTONNEW 1018 -#define wxID_BUTTONEDIT 1019 -#define wxID_BUTTONDELETE 1020 -#define wxID_DEL0 1021 -#define wxID_DEL1 1022 -#define wxID_DEL2 1023 -#define wxID_DEL3 1024 -#define wxID_DEL4 1025 -#define wxID_DEL5 1026 -#define wxID_DEL6 1027 -#define wxID_DEL7 1028 -#define wxID_DEL8 1029 -#define wxID_DEL9 1030 -#define wxID_DEL10 1031 -#define wxID_DEL11 1032 -#define wxID_DEL12 1033 -#define wxID_DEL13 1034 -#define wxID_DEL14 1035 -#define wxID_DEL15 1036 -#define wxID_DEL16 1037 -#define wxID_DEL17 1038 -#define wxID_DEL18 1039 -#define wxID_DEL19 1040 -#define wxID_BUTTONPREVIEW 1041 -#define wxID_BUTTONSAMPLE 1042 -#define wxID_CANCEL2 1043 -#define wxID_BUTTONBACK 1044 -#define wxID_BUTTONNEXT 1045 -#define wxID_SUBMIT 1046 -#define wxID_TEXTCTRL 1047 +#define wxID_MENUOPTIONSOPTIONS 1003 +#define wxID_BUTTONSEND 1004 +#define wxID_BUTTONRECEIVE 1005 +#define wxID_TEXTCTRLADDRESS 1006 +#define wxID_BUTTONCOPY 1007 +#define wxID_BUTTONCHANGE 1008 +#define wxID_TRANSACTIONFEE 1009 +#define wxID_PROXYIP 1010 +#define wxID_PROXYPORT 1011 +#define wxID_TEXTCTRLPAYTO 1012 +#define wxID_BUTTONPASTE 1013 +#define wxID_BUTTONADDRESSBOOK 1014 +#define wxID_TEXTCTRLAMOUNT 1015 +#define wxID_CHOICETRANSFERTYPE 1016 +#define wxID_LISTCTRL 1017 +#define wxID_BUTTONRENAME 1018 +#define wxID_BUTTONNEW 1019 +#define wxID_BUTTONEDIT 1020 +#define wxID_BUTTONDELETE 1021 +#define wxID_DEL0 1022 +#define wxID_DEL1 1023 +#define wxID_DEL2 1024 +#define wxID_DEL3 1025 +#define wxID_DEL4 1026 +#define wxID_DEL5 1027 +#define wxID_DEL6 1028 +#define wxID_DEL7 1029 +#define wxID_DEL8 1030 +#define wxID_DEL9 1031 +#define wxID_DEL10 1032 +#define wxID_DEL11 1033 +#define wxID_DEL12 1034 +#define wxID_DEL13 1035 +#define wxID_DEL14 1036 +#define wxID_DEL15 1037 +#define wxID_DEL16 1038 +#define wxID_DEL17 1039 +#define wxID_DEL18 1040 +#define wxID_DEL19 1041 +#define wxID_BUTTONPREVIEW 1042 +#define wxID_BUTTONSAMPLE 1043 +#define wxID_CANCEL2 1044 +#define wxID_BUTTONBACK 1045 +#define wxID_BUTTONNEXT 1046 +#define wxID_SUBMIT 1047 +#define wxID_TEXTCTRL 1048 /////////////////////////////////////////////////////////////////////////////// /// Class CMainFrameBase @@ -203,7 +204,6 @@ class COptionsDialogBase : public wxDialog wxStaticText* m_staticText35; wxCheckBox* m_checkBoxStartOnSystemStartup; wxCheckBox* m_checkBoxMinimizeToTray; - wxCheckBox* m_checkBoxMinimizeOnClose; wxCheckBox* m_checkBoxUseProxy; diff --git a/uiproject.fbp b/uiproject.fbp index 3aa1c86bc..58a99bddd 100644 --- a/uiproject.fbp +++ b/uiproject.fbp @@ -70,7 +70,7 @@ - + 240,240,240 1 @@ -193,7 +193,7 @@ 0 1 - wxID_ANY + wxID_MENUOPTIONSOPTIONS wxITEM_NORMAL &Options... m_menuOptionsOptions @@ -2319,7 +2319,7 @@ 0 wxID_ANY - &Minimize to the system tray instead of the taskbar + &Minimize to the tray instead of the taskbar m_checkBoxMinimizeToTray @@ -2360,75 +2360,54 @@ 5 - wxEXPAND - 1 - + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + + 0 + + 1 + + + 0 + wxID_ANY + M&inimize to the tray on close + - bSizer101 - wxHORIZONTAL - none - - 5 - - 0 - - 0 - protected - 16 - - - - 5 - wxALL|wxALIGN_CENTER_VERTICAL - 0 - - - 0 - - 1 - - - 0 - wxID_ANY - Mi&nimize to system tray on close - - - m_checkBoxMinimizeOnClose - protected - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + m_checkBoxMinimizeOnClose + protected + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util.cpp b/util.cpp index 4a5b983d2..7a947730c 100644 --- a/util.cpp +++ b/util.cpp @@ -85,14 +85,14 @@ void RandAddSeed() void RandAddSeedPerfmon() { -#ifdef __WXMSW__ - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // This can take up to 2 seconds, so only do it every 10 minutes static int64 nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) return; nLastPerfmon = GetTime(); +#ifdef __WXMSW__ + // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data unsigned char pdata[250000]; memset(pdata, 0, sizeof(pdata)); @@ -109,9 +109,30 @@ void RandAddSeedPerfmon() printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str(), nSize); } +#else + printf("%s RandAddSeed()\n", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); #endif } +uint64 GetRand(uint64 nMax) +{ + if (nMax == 0) + return 0; + + // The range of the random source must be a multiple of the modulus + // to give every possible output value an equal possibility + uint64 nRange = (UINT64_MAX / nMax) * nMax; + uint64 nRand = 0; + do + RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); + while (nRand >= nRange); + return (nRand % nMax); +} + + + + + @@ -449,28 +470,6 @@ string GetDataDir() -uint64 GetRand(uint64 nMax) -{ - if (nMax == 0) - return 0; - - // The range of the random source must be a multiple of the modulus - // to give every possible output value an equal possibility - uint64 nRange = (_UI64_MAX / nMax) * nMax; - uint64 nRand = 0; - do - RAND_bytes((unsigned char*)&nRand, sizeof(nRand)); - while (nRand >= nRange); - return (nRand % nMax); -} - - - - - - - - // @@ -483,7 +482,6 @@ uint64 GetRand(uint64 nMax) // note: NTP isn't implemented yet, so until then we just use the median // of other nodes clocks to correct ours. // - int64 GetTime() { return time(NULL); diff --git a/util.h b/util.h index 8fcfcd0dc..ddac44944 100644 --- a/util.h +++ b/util.h @@ -54,9 +54,13 @@ inline T& REF(const T& val) return (T&)val; } -#ifndef __WXMSW__ -#define _UI64_MAX UINT64_MAX -#define _I64_MAX INT64_MAX +#ifdef __WXMSW__ +#define MSG_NOSIGNAL 0 +#define MSG_DONTWAIT 0 +#define UINT64_MAX _UI64_MAX +#define INT64_MAX _I64_MAX +#define INT64_MIN _I64_MIN +#else #define WSAGetLastError() errno #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEMSGSIZE EMSGSIZE @@ -74,18 +78,6 @@ typedef u_int SOCKET; #define MAX_PATH 1024 #define Sleep(n) wxMilliSleep(n) #define Beep(n1,n2) (0) -inline int _beginthread(void(*pfn)(void*), unsigned nStack, void* parg) { thread(bind(pfn, parg)); return 0; } -inline void _endthread() { pthread_exit(NULL); } -inline int GetCurrentThread() { return 0; } -// threads are processes on linux, so setpriority affects just the one thread -inline void SetThreadPriority(int nThread, int nPriority) { setpriority(PRIO_PROCESS, getpid(), nPriority); } -#define THREAD_PRIORITY_LOWEST PRIO_MIN -#define THREAD_PRIORITY_BELOW_NORMAL 2 -#define THREAD_PRIORITY_NORMAL 0 -#define THREAD_PRIORITY_ABOVE_NORMAL 0 -#endif -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 #endif @@ -133,6 +125,7 @@ void AddTimeData(unsigned int ip, int64 nTime); + // Wrapper to automatically initialize critical sections class CCriticalSection { @@ -201,8 +194,6 @@ public: - - inline int OutputDebugStringF(const char* pszFormat, ...) { int ret = 0; @@ -498,3 +489,83 @@ inline uint160 Hash160(const vector& vch) RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2); return hash2; } + + + + + + + + + + + +// Note: It turns out we might have been able to use boost::thread +// by using TerminateThread(boost::thread.native_handle(), 0); +#ifdef __WXMSW__ +typedef HANDLE pthread_t; + +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + DWORD nUnused = 0; + HANDLE hthread = + CreateThread( + NULL, // default security + 0, // inherit stack size from parent + (LPTHREAD_START_ROUTINE)pfn, // function pointer + parg, // argument + 0, // creation option, start immediately + &nUnused); // thread identifier + if (hthread == NULL) + { + printf("Error: CreateThread() returned %d\n", GetLastError()); + return (pthread_t)0; + } + if (!fWantHandle) + { + CloseHandle(hthread); + return (pthread_t)-1; + } + return hthread; +} + +inline void SetThreadPriority(int nPriority) +{ + SetThreadPriority(GetCurrentThread(), nPriority); +} +#else +inline pthread_t CreateThread(void(*pfn)(void*), void* parg, bool fWantHandle=false) +{ + pthread_t hthread = 0; + int ret = pthread_create(&hthread, NULL, (void*(*)(void*))pfn, parg); + if (ret != 0) + { + printf("Error: pthread_create() returned %d\n", ret); + return (pthread_t)0; + } + if (!fWantHandle) + return (pthread_t)-1; + return hthread; +} + +#define THREAD_PRIORITY_LOWEST PRIO_MIN +#define THREAD_PRIORITY_BELOW_NORMAL 2 +#define THREAD_PRIORITY_NORMAL 0 +#define THREAD_PRIORITY_ABOVE_NORMAL 0 + +inline void SetThreadPriority(int nPriority) +{ + // threads are processes on linux, so PRIO_PROCESS affects just the one thread + setpriority(PRIO_PROCESS, getpid(), nPriority); +} + +inline bool TerminateThread(pthread_t hthread, unsigned int nExitCode) +{ + return (pthread_cancel(hthread) == 0); +} + +inline void ExitThread(unsigned int nExitCode) +{ + pthread_exit((void*)nExitCode); +} +#endif