219 lines
10 KiB
HTML
219 lines
10 KiB
HTML
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||
|
<html>
|
||
|
<head>
|
||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||
|
<meta name="Copyright" content="Copyright (c) 2006 by IBM Corporation">
|
||
|
<title>Powerful i5/OS cleanup mechanisms allow application deadlock
|
||
|
(cancel_handler and C++ automatic destructors)</title>
|
||
|
<!-- Begin Header Records ========================================== -->
|
||
|
<!-- All rights reserved. Licensed Materials Property of IBM -->
|
||
|
<!-- US Government Users Restricted Rights -->
|
||
|
<!-- Use, duplication or disclosure restricted by -->
|
||
|
<!-- GSA ADP Schedule Contract with IBM Corp. -->
|
||
|
<!-- Change History: -->
|
||
|
<!-- YYMMDD USERID Change description -->
|
||
|
<!-- NETMG2 SCRIPT A converted by B2H R4.1 (346) (CMS) by HOLTJM at -->
|
||
|
<!-- RCHVMW2 on 29 Jan 1999 at 10:01:37 -->
|
||
|
<!--File Edited November 2001 -->
|
||
|
<!--End Header Records -->
|
||
|
<link rel="stylesheet" type="text/css" href="../rzahg/ic.css">
|
||
|
</head>
|
||
|
<body>
|
||
|
<!-- Java sync-link -->
|
||
|
<script language="Javascript" src="../rzahg/synch.js" type="text/javascript">
|
||
|
</script>
|
||
|
|
||
|
<a name="Top_Of_Page"></a>
|
||
|
|
||
|
<h2>Powerful i5/OS cleanup mechanisms allow application deadlock
|
||
|
(cancel_handler and C++ automatic destructors)</h2>
|
||
|
|
||
|
<p>i5/OS provides a set of powerful cleanup mechanisms. In i5/OS, an
|
||
|
application has the ability to register a cancel handler. Your application can
|
||
|
enable a cancel handler by using the #pragma cancel_handler preprocessor
|
||
|
statement if it is written in C or C++ or by using the <strong>
|
||
|
CEERTX</strong>() API.</p>
|
||
|
|
||
|
<p>A cancel handler is similar to a Pthread cancellation cleanup handler.
|
||
|
However, a cancel handler runs whenever the stack frame or function for which
|
||
|
it was registered ends in any way other than a normal return. Pthread
|
||
|
cancellation cleanup handlers run only when the thread is terminated with
|
||
|
<strong>pthread_exit</strong>() or <strong>pthread_cancel</strong>() or when
|
||
|
the thread returns from the threads start routine.</p>
|
||
|
|
||
|
<p>The cancel handler is guaranteed to run for all conditions that cause the
|
||
|
stack frame to end (other than return), such as thread termination, job
|
||
|
termination, calls to <strong>exit</strong>(), <strong>abort</strong>(),
|
||
|
exceptions that percolate up the stack, and cancel stack frames. Similarly, C++
|
||
|
destructors for automatic C++ objects are guaranteed to run when the stack
|
||
|
frame (function) or scope in which it was registered ends.</p>
|
||
|
|
||
|
<p>These mechanisms ensure that your application can always clean up its
|
||
|
resources. With the added power of these mechanisms, an application can easily
|
||
|
cause a deadlock.</p>
|
||
|
|
||
|
<p>The following is an example of such a problem:</p>
|
||
|
<blockquote>
|
||
|
<p>An application has a function <strong>foo</strong>() that
|
||
|
registers a cancel handler called <strong>cleanup</strong>(). The function
|
||
|
<strong>foo</strong>() is called by multiple threads in the application. The
|
||
|
application is ended abnormally with a call to <strong>abort</strong>() or by
|
||
|
system operator intervention (with the <strong>ENDJOB *IMMED</strong> CL
|
||
|
command). When this job is ended, every thread is immediately terminated. When
|
||
|
the system terminates a thread by terminating each call stack entry in the
|
||
|
thread, it eventually reaches the function <strong>foo</strong>() in that
|
||
|
thread. When function <strong>foo</strong>() is reached, the system recognizes
|
||
|
that it must not remove that function from the call stack without running the
|
||
|
function <strong>cleanup</strong>(), and so the system runs <strong>
|
||
|
cleanup</strong>(). Because your application is multithreaded, all of the job
|
||
|
ending and cleanup processing proceeds in parallel in each thread. Also,
|
||
|
because <strong>abort</strong>() or <strong>ENDJOB *IMMED</strong> was used,
|
||
|
the current state and location of each thread in your application is cannot be
|
||
|
determined. When the <strong>cleanup</strong>() function runs, it is very
|
||
|
difficult for the application to correctly assume that any specific cleanup can
|
||
|
be done. Any resources that the <strong>cleanup</strong>() function attempts to
|
||
|
acquire may be held by other threads in the process, other jobs in the system,
|
||
|
or possibly by the same thread running the <strong>cleanup</strong>() function.
|
||
|
The state of application variables or resources that your application
|
||
|
manipulates may be in an inconsistent state because the call to <strong>
|
||
|
abort</strong>() or <strong>ENDJOB *IMMED</strong> asynchronously interrupted
|
||
|
every thread in the process at the same time. The application can easily reach
|
||
|
a deadlock when running the cancel handlers or C++ destructors.</p>
|
||
|
|
||
|
<p>Do not attempt to acquire locks or resources in cancel handlers or C++
|
||
|
automatic object destructors without preparing for the possibility that the
|
||
|
resources cannot be acquired.</p>
|
||
|
</blockquote>
|
||
|
|
||
|
<br>
|
||
|
<h3>Important</h3>
|
||
|
|
||
|
<p>Neither a cancel handler nor a destructor for a C++ object can prevent the
|
||
|
call stack entry from being terminated, but the termination of the call stack
|
||
|
entry (and therefore the job or thread) is delayed until the cancel handler or
|
||
|
destructor completes.</p>
|
||
|
|
||
|
<p>If the cancel handler or destructor does not complete, the system does not
|
||
|
continue terminating the call stack entry (and possibly the job or thread). The
|
||
|
only alternative at this point is to use the <strong>WRKJOB</strong> CL command
|
||
|
(option 20) to end the thread, or the <strong>ENDJOB *IMMED</strong> CL
|
||
|
command. If the <strong>ENDJOB *IMMED</strong> command causes a cancel handler
|
||
|
to run in the first place, the only option left is the <strong>
|
||
|
ENDJOBABN</strong> CL command because any remaining cancel handlers are still
|
||
|
guaranteed to run.</p>
|
||
|
|
||
|
<p>The <strong>ENDJOBABN</strong> CL command is not recommended. The <strong>
|
||
|
ENDJOBABN</strong> command causes the job to be terminated with no further
|
||
|
cleanup allowed (application or operating system). If the application is
|
||
|
suspended while trying to access certain operating system resources, those
|
||
|
resources may be damaged. If operating system resources are damaged, you may
|
||
|
need to take various reclaim, deletion, or recovery steps and, in extreme
|
||
|
conditions, restart the system.</p>
|
||
|
|
||
|
<br>
|
||
|
<h3>Recommendations</h3>
|
||
|
|
||
|
<p>If you want to cleanup your job or application, you can use one of the
|
||
|
following mechanisms:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>If you want to do process level or activation group cleanup for normal
|
||
|
termination, use the C <strong>atexit</strong>() function to register your
|
||
|
cleanup function. The <strong>atexit</strong>() function provides a mechanism
|
||
|
to run cleanup after the activation group and possibly the threads, are
|
||
|
terminated. This action significantly reduces the complexity.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>If you always want a chance to do process level or activation group cleanup
|
||
|
in all cases (normal and abnormal), you could use the Register Activation Group
|
||
|
Exit (<strong>CEE4RAGE</strong>()) system API. The <strong>CEE4RAGE()</strong>
|
||
|
function provides a mechanism to run cleanup after the activation group (and
|
||
|
possibly the threads) are terminated. This action significantly reduces the
|
||
|
complexity.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>You can safely use cancel handlers. Simplify your cancel handlers so that
|
||
|
they only unlock or release resources and do not attempt to acquire any new
|
||
|
resources or locks.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>You can remove your cancel handlers and create a CL command, program, or
|
||
|
tool that terminates your application in a more controlled fashion:<br>
|
||
|
<br>
|
||
|
|
||
|
|
||
|
<ul>
|
||
|
<li>One possibility is a tool that uses a signal to terminate the application.
|
||
|
When the signal comes in, your application can get control in a single location
|
||
|
(preferably by using the <strong>sigwait</strong>() API to safely and
|
||
|
synchronously get the signal), and then perform some level of cleanup. Then it
|
||
|
can use <strong>exit</strong>() or <strong>abort</strong>() to end the
|
||
|
application from within. Often this action is sufficient to remove the
|
||
|
complexity.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>A second possibility is to use the <strong>ENDJOB *CNTRLD</strong> CL
|
||
|
command and have your application dedicate a thread to watching for the
|
||
|
controlled end condition. The application thread can use the <strong>
|
||
|
QUSRJOBI</strong> (Get Job Information) or the <strong>QWCRTVCA</strong>
|
||
|
(Retrieve Current Attributes) APIs to look at the <strong>End Status</strong>
|
||
|
information associated with your job. The <strong>End Status</strong> indicates
|
||
|
that the job is ending in a controlled fashion, and your application can take
|
||
|
safe and synchronous steps to clean up and exit.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>A third possibility is to use the asynchronous signals support and set up a
|
||
|
handler for the SIGTERM asynchronous signal. Support has been added to the
|
||
|
system so that, if an ENDJOB *CNTRLD is done and the target job has a handler
|
||
|
registered for the SIGTERM signal, that signal gets delivered to the target
|
||
|
job. You should dedicate a thread for handling signals by using the <strong>
|
||
|
sigwait()</strong> API in the dedicated thread. When the signal handling thread
|
||
|
detects a SIGTERM signal using the <strong>sigwait()</strong> API, it can
|
||
|
safely clean up and terminate the application. The system support for the
|
||
|
delivery of the SIGTERM signal when ENDJOB *CNTRLD is issued was added in the
|
||
|
base OS/400 in Version 4 Release 3 Modification 0 and is also available in
|
||
|
Version 4 Release 2 Modification 0 through program temporary fixes (PTFs)
|
||
|
5769SS1-SF47161 and 5769SS1-SF47175. For more information about ending your
|
||
|
job, see the <a href="../rzaks/rzaks1.htm">Work Management</a> topic.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
|
||
|
<li>A fourth possibility includes other interprocess communications (IPC)
|
||
|
mechanisms that can also be used to indicate that your application should
|
||
|
terminate in a safe and controlled fashion.<br>
|
||
|
<br>
|
||
|
</li>
|
||
|
</ul>
|
||
|
</li>
|
||
|
|
||
|
<li>If you want to do thread-level cleanup, use the pthread APIs, such as
|
||
|
<strong>pthread_cleanup_push</strong>(), <strong>
|
||
|
pthread_cleanup_pop</strong>(), and <strong>pthread_key_create</strong>() to
|
||
|
create cancellation cleanup functions that run when the thread terminates under
|
||
|
normal conditions. Often your cleanup functions do not need to run when the job
|
||
|
ends. The most common use for these functions is to free heap storage or unlock
|
||
|
resources. Unlocking resources is safe in a cancel handler, and you do not need
|
||
|
to use <strong>free</strong>() on heap storage when the entire job is ending
|
||
|
anyway.</li>
|
||
|
</ul>
|
||
|
|
||
|
<hr>
|
||
|
<center>
|
||
|
<table cellpadding="2" cellspacing="2">
|
||
|
<tr align="center">
|
||
|
<td valign="middle" align="center">
|
||
|
<a href="#Top_Of_Page">Top</a> |
|
||
|
<a href="rzah4mst.htm">Pthread APIs</a> |
|
||
|
<a href="aplist.htm">APIs by category</a></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
</center>
|
||
|
</body>
|
||
|
</html>
|
||
|
|