ncbi_process.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:14k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: ncbi_process.cpp,v $
  4.  * PRODUCTION Revision 1000.3  2004/06/01 19:08:38  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.8
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: ncbi_process.cpp,v 1000.3 2004/06/01 19:08:38 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Authors:  Aaron Ucko, Vladimir Ivanov
  35.  *
  36.  *
  37.  */
  38. #include <ncbi_pch.hpp>
  39. #include <corelib/ncbifile.hpp>
  40. #include <corelib/ncbi_system.hpp>
  41. #include <corelib/ncbi_process.hpp>
  42. #include <corelib/ncbi_safe_static.hpp>
  43. #if defined(NCBI_OS_MSWIN)
  44. #  include <process.h>
  45. #elif defined(NCBI_OS_UNIX)
  46. #  include <sys/types.h>
  47. #  include <signal.h>
  48. #  include <sys/wait.h>
  49. #  include <errno.h>
  50. #  include <unistd.h>
  51. #endif
  52. BEGIN_NCBI_SCOPE
  53. /////////////////////////////////////////////////////////////////////////////
  54. //
  55. // CProcess 
  56. //
  57. // Predefined timeouts (in microseconds)
  58. const unsigned long kWaitPrecision = 100; 
  59. const unsigned long CProcess::kDefaultKillTimeout = 1000;
  60. CProcess::CProcess(long process, EProcessType type)
  61.     : m_Process(process), m_Type(type)
  62. {
  63.     return;
  64. }
  65. #if defined(NCBI_OS_MSWIN)
  66. // The helper constructor for MS Windows to avoid cast from HANDLE to long
  67. CProcess::CProcess(HANDLE process, EProcessType type)
  68.     : m_Process((long)process), m_Type(type)
  69. {
  70.     return;
  71. }
  72. #endif
  73. TPid CProcess::GetCurrentPid(void)
  74. {
  75. #ifdef NCBI_OS_UNIX
  76.     return getpid();
  77. #elif defined(NCBI_OS_MSWIN)
  78.     return GetCurrentProcessId();
  79. #else
  80. #  error "Not implemented on this platform"
  81. #endif
  82. }
  83. bool CProcess::IsAlive(void) const
  84. {
  85. #ifdef NCBI_OS_UNIX
  86.     if ( kill((TPid)m_Process, 0) < 0  &&  errno != EPERM ) {
  87.         return false;
  88.     }
  89.     return true;
  90. #elif defined(NCBI_OS_MSWIN)
  91.     HANDLE hProcess = 0;
  92.     if (m_Type == ePid) {
  93.         hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, m_Process);
  94.         if (!hProcess) {
  95.             return GetLastError() == ERROR_ACCESS_DENIED;
  96.         }
  97.     } else {
  98.         hProcess = (HANDLE)m_Process;
  99.     }
  100.     DWORD status = 0;
  101.     _ASSERT(STILL_ACTIVE != 0);
  102.     GetExitCodeProcess(hProcess, &status);
  103.     if (m_Type == ePid) {
  104.         CloseHandle(hProcess);
  105.     }
  106.     return status == STILL_ACTIVE;
  107. #else
  108. #  error "Not implemented on this platform"
  109. #endif
  110. }
  111. bool CProcess::Kill(unsigned long timeout) const
  112. {
  113. #ifdef NCBI_OS_UNIX
  114.     TPid pid = (TPid)m_Process;
  115.     int status;
  116.     // Try to kill process
  117.     if (kill(pid, SIGTERM) == -1  &&  errno == EPERM) {
  118.         return false;
  119.     }
  120.     // Check process termination within timeout
  121.     do {
  122.         if (waitpid(pid, &status, WNOHANG) == pid) {
  123.             // Release zombie process from the system
  124.             waitpid(pid, &status, 0);
  125.             return true;
  126.         }
  127.         unsigned long x_sleep = kWaitPrecision;
  128.         if (timeout < kWaitPrecision) {
  129.             x_sleep = timeout;
  130.         }
  131.         if ( x_sleep ) {
  132.             timeout -= x_sleep;
  133.             SleepMilliSec(x_sleep);
  134.         }
  135.     } while (timeout);
  136.     // Try harder to kill stubborn process
  137.     if (kill(pid, SIGKILL) == -1  &&  errno == EPERM) {
  138.         return false;
  139.     }
  140.     // Release zombie process from the system
  141.     if (waitpid(pid, &status, WNOHANG) == pid) {
  142.         waitpid(pid, &status, 0);
  143.     }
  144.     return true;
  145. #elif defined(NCBI_OS_MSWIN)
  146.     HANDLE hProcess    = NULL;
  147.     HANDLE hThread     = NULL;
  148.     bool   enable_sync = true;
  149.     // Get process handle
  150.     if (m_Type == ePid) {
  151.         hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_TERMINATE |
  152.                                SYNCHRONIZE, FALSE, m_Process);
  153.         if ( !hProcess ) {
  154.             enable_sync = false;
  155.             hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, m_Process);
  156.             if (!hProcess   &&  GetLastError() == ERROR_ACCESS_DENIED) {
  157.                 return false;
  158.             }
  159.         }
  160.     } else {
  161.         hProcess = (HANDLE)m_Process;
  162.     }
  163.     // Check process handle
  164.     if ( !hProcess  ||  hProcess == INVALID_HANDLE_VALUE ) {
  165.         return true;
  166.     }
  167.     // Terminate process
  168.     bool terminated = false;
  169.     try {
  170.         // Safe process termination
  171.         // (kernel32.dll loaded at same address in each process)
  172.         FARPROC exitproc = GetProcAddress(GetModuleHandle("KERNEL32.DLL"),
  173.                                           "ExitProcess");
  174.         if ( exitproc ) {
  175.             hThread = CreateRemoteThread(hProcess, NULL, 0,
  176.                                          (LPTHREAD_START_ROUTINE)exitproc,
  177.                                          0, 0, 0);
  178.         }
  179.         // Wait for process terminated, or timeout expired
  180.         if (enable_sync  &&  timeout) {
  181.             if (WaitForSingleObject(hProcess, timeout) == WAIT_OBJECT_0) {
  182.                 throw true;
  183.             }
  184.         }
  185.         // Try harder to kill stubborn process
  186.         if ( TerminateProcess(hProcess, -1) != 0  ||
  187.              GetLastError() == ERROR_INVALID_HANDLE ) {
  188.             // If process terminated succesfuly or error occur but
  189.             // process handle became invalid -- process has terminated
  190.             terminated = true;
  191.         }
  192.     }
  193.     catch (bool e) {
  194.         terminated = e;
  195.     }
  196.     // Close opened temporary process handle
  197.     if ( hThread ) {
  198.         CloseHandle(hThread);
  199.     }
  200.     if (m_Type == ePid ) {
  201.         CloseHandle(hProcess);
  202.     }
  203.     return terminated;
  204. #else
  205. #  error "Not implemented on this platform"
  206. #endif
  207. }
  208. int CProcess::Wait(unsigned long timeout) const
  209. {
  210. #if defined(NCBI_OS_UNIX)
  211.     TPid  pid     = (TPid)m_Process;
  212.     int   options = (timeout == kMax_ULong /*infinite*/) ? 0 : WNOHANG;
  213.     int   status;
  214.     // Check process termination within timeout (or infinite)
  215.     do {
  216.         TPid ws = waitpid(pid, &status, options);
  217.         if (ws > 0) {
  218.             // Process has terminated.
  219.             if ( options ) {
  220.                 // Release zombie process from the system.
  221.                 waitpid(pid, &status, 0);
  222.             }
  223.             return WEXITSTATUS(status);
  224.         } 
  225.         else if (ws < 0  &&  errno != EINTR ) {
  226.             // Some error
  227.             break;
  228.         }
  229.         // Process is still running
  230.         unsigned long x_sleep = kWaitPrecision;
  231.         if (timeout != kMax_ULong /*infinite*/) {
  232.             if (timeout < kWaitPrecision) {
  233.                 x_sleep = timeout;
  234.             }
  235.             timeout -= x_sleep;
  236.         }
  237.         if ( x_sleep ) {
  238.             SleepMilliSec(x_sleep);
  239.         }
  240.     } while (timeout); 
  241.     return -1;
  242. #elif defined(NCBI_OS_MSWIN)
  243.     HANDLE hProcess;
  244.     bool   enable_sync = true;
  245.     // Get process handle
  246.     if (m_Type == ePid) {
  247.         hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
  248.                                FALSE, m_Process);
  249.         if ( !hProcess ) {
  250.             enable_sync = false;
  251.             hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, m_Process);
  252.             if (!hProcess   &&  GetLastError() == ERROR_ACCESS_DENIED) {
  253.                 return false;
  254.             }
  255.         }
  256.     } else {
  257.         hProcess = (HANDLE)m_Process;
  258.     }
  259.     DWORD status = -1;
  260.     try {
  261.         // Is process still running?
  262.         if ( !hProcess  || hProcess == INVALID_HANDLE_VALUE ) {
  263.             throw -1;
  264.         }
  265.         if ( GetExitCodeProcess(hProcess, &status)  &&
  266.              status != STILL_ACTIVE ) {
  267.             throw (int)status;
  268.         }
  269.         // Wait for process termination, or timeout expired
  270.         if (enable_sync  &&  timeout) {
  271.             DWORD tv = (timeout == kMax_ULong) ? INFINITE : (DWORD)timeout;
  272.             if (WaitForSingleObject(hProcess, tv) != WAIT_OBJECT_0) {
  273.                 throw -1;
  274.             }
  275.         }
  276.         // Get process exit code
  277.         if ( !GetExitCodeProcess(hProcess, &status) ) {
  278.             throw -1;
  279.         }
  280.     }
  281.     catch (int e) {
  282.         status = e;
  283.     }
  284.     if (m_Type == ePid ) {
  285.         CloseHandle(hProcess);
  286.     }
  287.     return (int)status;
  288. #else
  289. #  error "Not implemented on this platform"
  290. #endif
  291. }
  292. /////////////////////////////////////////////////////////////////////////////
  293. //
  294. // CPIDGuard
  295. //
  296. // Protective mutex
  297. DEFINE_STATIC_FAST_MUTEX(s_PidGuardMutex);
  298. // NOTE: This method to protect PID file works only within one process.
  299. //       CPIDGuard know nothing about PID file modification or deletion 
  300. //       by other processes. Be aware.
  301. CPIDGuard::CPIDGuard(const string& filename, const string& dir)
  302.     : m_OldPID(0), m_NewPID(0)
  303. {
  304.     string real_dir;
  305.     CDirEntry::SplitPath(filename, &real_dir, 0, 0);
  306.     if (real_dir.empty()) {
  307.         if (dir.empty()) {
  308.             real_dir = CDir::GetTmpDir();
  309.         } else {
  310.             real_dir = dir;
  311.         }
  312.         m_Path = CDirEntry::MakePath(real_dir, filename);
  313.     } else {
  314.         m_Path = filename;
  315.     }
  316.     UpdatePID();
  317. }
  318. CPIDGuard::~CPIDGuard(void)
  319. {
  320.     Release();
  321. }
  322. void CPIDGuard::Release(void)
  323. {
  324.     if ( !m_Path.empty() ) {
  325.         // MT-Safe protect
  326.         CFastMutexGuard LOCK(s_PidGuardMutex);
  327.         // Read info
  328.         TPid pid = 0;
  329.         unsigned int ref = 0;
  330.         CNcbiIfstream in(m_Path.c_str());
  331.         if ( in.good() ) {
  332.             in >> pid >> ref;
  333.             in.close();
  334.             if ( m_NewPID != pid ) {
  335.                 // We do not own this file more
  336.                 return;
  337.             }
  338.             if ( ref ) {
  339.                 ref--;
  340.             }
  341.             // Check reference counter
  342.             if ( ref ) {
  343.                 // Write updated reference counter into the file
  344.                 CNcbiOfstream out(m_Path.c_str(),
  345.                                   IOS_BASE::out | IOS_BASE::trunc);
  346.                 if ( out.good() ) {
  347.                     out << pid << endl << ref << endl;
  348.                 }
  349.                 if ( !out.good() ) {
  350.                     NCBI_THROW(CPIDGuardException, eWrite,
  351.                                "Unable to write into PID file " + m_Path +": "
  352.                                + strerror(errno));
  353.                 }
  354.             } else {
  355.                 // Remove the file
  356.                 CDirEntry(m_Path).Remove();
  357.             }
  358.         }
  359.         m_Path.erase();
  360.     }
  361. }
  362. void CPIDGuard::UpdatePID(TPid pid)
  363. {
  364.     if (pid == 0) {
  365.         pid = CProcess::GetCurrentPid();
  366.     }
  367.     // MT-Safe protect
  368.     CFastMutexGuard LOCK(s_PidGuardMutex);
  369.     // Read old PID
  370.     unsigned int ref = 1;
  371.     CNcbiIfstream in(m_Path.c_str());
  372.     if ( in.good() ) {
  373.         in >> m_OldPID >> ref;
  374.         if ( m_OldPID == pid ) {
  375.             // Guard the same PID. Just increase the reference counter.
  376.             ref++;
  377.         } else {
  378.             if ( CProcess(m_OldPID,CProcess::ePid).IsAlive() ) {
  379.                 NCBI_THROW2(CPIDGuardException, eStillRunning,
  380.                             "Process is still running", m_OldPID);
  381.             }
  382.             ref = 1;
  383.         }
  384.     }
  385.     in.close();
  386.     // Write new PID
  387.     CNcbiOfstream out(m_Path.c_str(), IOS_BASE::out | IOS_BASE::trunc);
  388.     if ( out.good() ) {
  389.         out << pid << endl << ref << endl;
  390.     }
  391.     if ( !out.good() ) {
  392.         NCBI_THROW(CPIDGuardException, eWrite,
  393.                    "Unable to write into PID file " + m_Path + ": "
  394.                    + strerror(errno));
  395.     }
  396.     // Save updated pid
  397.     m_NewPID = pid;
  398. }
  399. END_NCBI_SCOPE
  400. /*
  401.  * ===========================================================================
  402.  * $Log: ncbi_process.cpp,v $
  403.  * Revision 1000.3  2004/06/01 19:08:38  gouriano
  404.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.8
  405.  *
  406.  * Revision 1.8  2004/05/18 17:01:15  ivanov
  407.  * CProcess::
  408.  *     + WaitForAlive(), WaitForTerminate().
  409.  *     Fixed UNIX version Wait() to correct work with infinite timeouts.
  410.  * CPIDGuard::
  411.  *     Fixed CPIDGuard to use reference counters in PID file.
  412.  *     Added CPIDGuard::Remove().
  413.  *
  414.  * Revision 1.7  2004/05/14 13:59:26  gorelenk
  415.  * Added include of ncbi_pch.hpp
  416.  *
  417.  * Revision 1.6  2004/03/04 19:08:36  ivanov
  418.  * Fixed CProcess::IsAlive()
  419.  *
  420.  * Revision 1.5  2003/12/04 18:45:35  ivanov
  421.  * Added helper constructor for MS Windows to avoid cast from HANDLE to long
  422.  *
  423.  * Revision 1.4  2003/12/03 17:03:01  ivanov
  424.  * Fixed Kill() to handle zero timeouts
  425.  *
  426.  * Revision 1.3  2003/10/01 20:23:26  ivanov
  427.  * Added const specifier to CProcess class member functions
  428.  *
  429.  * Revision 1.2  2003/09/25 17:18:21  ucko
  430.  * UNIX: +<unistd.h> for getpid()
  431.  *
  432.  * Revision 1.1  2003/09/25 16:53:41  ivanov
  433.  * Initial revision. CPIDGuard class moved from ncbi_system.cpp.
  434.  *
  435.  * ===========================================================================
  436.  */