ibm-information-center/dist/eclipse/plugins/i5OS.ic.apis_5.4.0.1/concept7.htm

219 lines
10 KiB
HTML
Raw Permalink Normal View History

2024-04-02 14:02:31 +00:00
<!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>