10.3 マルチスレッドおよびマルチコンテキストATMIアプリケーションの実装

10.3.1 マルチスレッド/マルチコンテキストATMIアプリケーションのプログラミング開始前のガイドライン

コーディングを開始する前に、次の内容や条件を満たしていることを確認してください:

10.3.1.1 マルチスレッドATMIアプリケーションに必要な条件

開発プロジェクトを開始する前に、開発環境が次の条件を満たしていることを確認します。

  • Oracle Tuxedoシステムでサポートされる適切なスレッド・パッケージが、オペレーティング・システムで提供されていることが必要です。

    Oracle Tuxedoシステムには、スレッド生成用のツールは提供されていませんが、他のオペレーティング・システムで提供される各種のスレッド・パッケージがサポートされています。スレッドを生成して同期するには、オペレーティング・システム固有の関数を使用する必要があります。オペレーティング・システムでサポートされているスレッド・パッケージを確認するには、『Oracle Tuxedoシステムのインストール』「Oracle Tuxedoプラットフォーム・データ・シート」を参照してください。

  • マルチスレッド・サーバーを使用している場合は、これらのサーバーで使用されるリソース・マネージャでスレッドがサポートされていることが必要です。

10.3.1.2 マルチスレッド・アプリケーションのプログラミングでの一般的な検討事項

マルチスレッド・プログラムは、十分に経験を持つプログラマがコーディングする必要があります。特に、次のようなマルチスレッド固有の設計に関する基本的な知識があることが必要です:

  • 複数スレッド間の同時実行性制御の必要性
  • ほどんどのインスタンスで静的変数の使用を避ける必要性
  • マルチスレッド・プログラムでシグナルを使用することにより発生する可能性のある問題

これらは検討事項の一部にすぎず、ここに記せないほど多くの検討事項があります。マルチスレッド・プログラムをコーディングするプログラマは、それらの検討事項を熟知していることが前提となります。これらの検討事項については、マルチスレッド・アプリケーションのプログラミングに関する書籍を参照してください。

10.3.1.3 同時実行性に関する検討事項

マルチスレッドを使用すると、1つのアプリケーションの異なるスレッドが同じ会話で並列処理を行うことができるようになります。この方法はお薦めしませんが、Oracle Tuxedoシステムで禁止されているわけではありません。異なるスレッドによって同じ会話で並列処理が行われると、システムは同時呼出しが任意の順序で行われたように動作します。

複数のスレッドを使ってプログラミングする場合、ミューテックスなどの同時実行性制御関数を使用して、スレッド間の並列処理を管理する必要があります。以下は、同時実行性制御が必要になる3つの例です。

  • マルチスレッドのスレッドが同じコンテキストで動作する場合、プログラマは関数が必要な順序で実行されるようにします。たとえば、すべてのRPC呼出しと会話が完了した後でのみ、tpcommit()を呼び出すようにします。これらすべてのRPC呼び出しまたは会話型呼出しが行われるスレッドとは別のスレッドからtpcommit()が呼び出される場合、アプリケーションで何らかの同時実行性制御が必要になります。
  • 同じように、tpacall()tpgetrply()の呼出しを別々のスレッドで行うことはできますが、アプリケーションが次のいずれかの条件を満たしている必要があります。
    • tpacall()が呼び出された後でtpgetrply()が呼び出されます。
    • tpacall()を呼び出す前にtpgetrply()が呼び出される場合、結果が管理されます。
  • 複数のスレッドが同じ会話で処理を行うことは可能です。ただし、異なるスレッドがtpsend()をほとんど同時に呼び出した場合、システムはそれらのtpsend()呼出しが任意の順序で行われたように動作します。アプリケーション・プログラマは、それを認識しておかなければなりません。

ほとんどのアプリケーションで最良の戦略は、1つの会話のすべての処理を1つのスレッドにまとめてコーディングすることです。次善の戦略は、同時実行性制御を使用して、これらの処理を連続して行うことです。

10.3.2 ATMIクライアントでマルチコンテキストを使用するためのコーディング

この項には次の情報が含まれます:

アプリケーションでトランザクションを使用する場合、トランザクションのマルチコンテキストの結果についても注目する必要があります。詳細は、「マルチスレッド/マルチコンテキストATMIアプリケーションにおけるトランザクションのコーディング規則」を参照してください。

ノート:

この項で示す手順とサンプル・コードは、Oracle Tuxedoシステムで提供されるC言語のライブラリ関数を参照します。それらに相当するCOBOLライブラリ関数も利用できます。詳細については、『Oracle Tuxedo COBOL関数リファレンス』を参照してください。

10.3.2.1 コンテキストの属性

コンテキストを使用する場合、コーディングで以下の事柄に注意してください。

  • 元のディスパッチ・スレッドが終了する前に、サーバー・ディスパッチ・コンテキストに現在関連付けられているアプリケーション生成のサーバー・スレッドがコンテキストを変更せずに終了すると、tpreturn()またはtpforward()が失敗します。スレッドを終了しても、自動的にtpsetctxt(3c)の呼出しがトリガーされてコンテキストがTPNULLCONTEXTに変更されることはありません。
  • プロセス内のすべてのコンテキストに、同じバッファ・タイプ・スイッチを使用します。
  • 他のタイプのデータ構造体と同じように、マルチスレッド・アプリケーションではOracle Tuxedoバッファを適切に使用する必要があります。つまり、次のいずれかが該当する場合、バッファを2つの呼出しで同時に使用することはできません:
    • 両方の呼出しでバッファを使用する場合
    • 両方の呼出しでバッファを解放する場合
    • 1つの呼出しでバッファを使用し、もう1つの呼出しでバッファを解放する場合
  • tpinit()を複数回呼び出して、複数アプリケーションに参加するか、または単一アプリケーションに複数の接続を行う場合、tpinit()を呼び出すたびに、確立されているセキュリティ・メカニズムを調整する必要があります。

10.3.2.2 初期化時のマルチコンテキストの設定

クライアントがアプリケーションに参加する準備ができたら、次のリストに示すように、TPMULTICONTEXTSフラグを設定してjoining-application.html#GUID-442A4ACC-9A40-4DF4-BD74-EF30D79940CA__GUID-3FBBF8D2-DC4A-436F-B2C2-4477EF68D5F1tpinit()を指定します。

クライアントのマルチコンテキスト・アプリケーションへの参加のサンプル・コードのリスト

#include <stdio.h>
#include <atmi.h>

TPINIT    *    tpinitbuf;

main()
{
      tpinitbuf = tpalloc(“TPINIT”, NULL, TPINITNEED(0));
      tpinitbuf->flags = TPMULTICONTEXTS;
             .
             .
             .

   if (tpinit (tpinitbuf) == -1) {
          ERROR_PROCESSING_CODE
   }
             .
             .
             .
}

新しいアプリケーションとの関連付けが生成され、TUXCONFIGまたはWSENVFILE/WSNADDR環境変数で指定されたOracle Tuxedoドメインに割り当てられます。

ノート:

1つのプロセスでは、tpinit()へのすべての呼出しにTPMULTICONTEXTSフラグを含めるか、またはtpinit()へのすべての呼出しにこのフラグを含めません。この規則には、例外が1つあります。つまり、tpterm()への呼出しが正常に終了して、クライアントのすべてのアプリケーション関連付けが終了した場合、次にtpinit()を呼び出すときに必ずしもTPMULTICONTEXTSフラグを含む必要のない状態にプロセスが復元されます。

10.3.2.3 マルチコンテキストATMIクライアントのセキュリティの実装

同じプロセス内の各アプリケーションとの関連付けには、別個にセキュリティ検証を行う必要があります。検証の内容は、アプリケーションで使用されているセキュリティ・メカニズムのタイプによって異なります。たとえば、Oracle Tuxedoアプリケーションでは、システム・レベルのパスワードまたはアプリケーション・パスワードを使用します。

マルチコンテキスト・アプリケーションのプログラマは、アプリケーションで使用するセキュリティのタイプを決定し、そのセキュリティをプロセス内の各アプリケーションとの関連付けに実装します。

10.3.2.4 ATMIクライアント終了前のスレッドの同期

クライアントをアプリケーションから切断する準備ができたら、tpterm()を呼び出します。ただし、マルチコンテキスト・アプリケーションでは、tpterm()を呼び出すと現在のコンテキストが破棄されることに注目してください。その場合、現在のコンテキストで動作しているすべてのスレッドが影響を受けます。アプリケーション・プログラマは、tpterm()が不意に呼び出されることがないように、複数のスレッドを使用する場合は注意してください。

まだ動作中のスレッドがあるコンテキストでは、tpterm()を呼び出さないようにします。そのような状況でtpterm()を呼び出すと、そのコンテキストと関連付けられていた他のスレッドが特別な無効コンテキスト状態になります。無効コンテキスト状態では、大部分のATMI関数を使用できなくなります。無効コンテキスト状態からスレッドを解放するには、tpsetctxt(3c)またはtpterm()を呼び出します。良く設計されたアプリケーションでは、無効コンテキスト状態が生じることはありません。

ノート:

Oracle Tuxedoシステムでは、COBOLアプリケーションのマルチスレッドはサポートされていません。

10.3.2.5 コンテキストの切替え

次は、2つのコンテキストからサービスを呼び出すクライアントで行われる処理のステップをまとめたものです。

  1. TUXCONFIG環境変数にfirstappで必要な値を設定します。
  2. TPMULTICONTEXTSフラグを設定してtpinit()を呼び出し、最初のアプリケーションに参加します。
  3. tpgetctxt(3c)を呼び出して、現在のコンテキストへのハンドルを取得します。
  4. tuxputenv()を呼び出して、TUXCONFIG環境変数の値をsecondappコンテキストに必要な値に切り替えます。
  5. TPMULTICONTEXTSフラグを設定してtpinit()を呼び出し、2番目のアプリケーションに参加します。
  6. tpgetctxt(3c)を呼び出して、現在のコンテキストへのハンドルを取得します。
  7. tpsetctxt(3c)を呼び出して、firstappコンテキストからコンテキストの切替えを開始します。
  8. firstappサービスを呼び出します。
  9. tpsetctxt(3c)を呼び出してクライアントをsecondappコンテキストに切り替え、secondappサービスを呼び出します。
  10. tpsetctxt(3c)を呼び出してクライアントをfirstappコンテキストに切り替え、firstappサービスを呼び出します。
  11. tpterm()を呼び出して、firstappコンテキストを終了します。
  12. tpsetctxt(3c)を呼び出してクライアントをsecondappコンテキストに切り替え、secondappサービスを呼び出します。
  13. tpterm()を呼び出して、secondappコンテキストを終了します。

次のサンプル・コードは、このステップを示しています。

ノート:

コードを簡単にするために、エラー・チェックは省略してあります。

クライアントでのコンテキストの切替えのサンプル・コードのリスト

#include <stdio.h>
#include "atmi.h"/* Oracle Tuxedo header file */

#if defined(__STDC__) || defined(__cplusplus)
main(int argc, char *argv[])
#else
main(argc, argv)
int argc;
char *argv[];
#endif
{

              TPINIT * tpinitbuf;
            TPCONTEXT_T firstapp_contextID, secondapp_contextID;
        /* Assume that TUXCONFIG is initially set to /home/firstapp/TUXCONFIG*/
               /* 
             * Attach to the Oracle Tuxedo system in multicontext mode.
             */
             tpinitbuf=tpalloc(“TPINIT”, NULL, TPINITNEED(0));
          tpinitbuf->flags = TPMULTICONTEXTS;

             if (tpinit((TPINIT *) tpinitbuf) == -1) {
            (void) fprintf(stderr, "Tpinit failed\n");
            exit(1);
             }

             /*
              * Obtain a handle to the current context.
              */
                tpgetctxt(&firstapp_contextID, 0);

                 /*
              * Use tuxputenv to change the value of TUXCONFIG,    
              * so we now tpinit to another application.
              */
              tuxputenv("TUXCONFIG=/home/second_app/TUXCONFIG");

              /*
               * tpinit to secondapp.
               */
               if (tpinit((TPINIT *) tpinitbuf) == -1) {
                        (void) fprintf(stderr, "Tpinit failed\n");
                        exit(1);
}
/*
 * Get a handle to the context of secondapp.
   */
tpgetctxt(&secondapp_contextID, 0);

/*
 * Now you can alternate between the two contexts
 * using tpsetctxt and the handles you obtained from
    * tpgetctxt. You begin with firstapp.
 */

tpsetctxt(firstapp_contextID, 0);

/*
    * You call services offered by firstapp and then switch
  * to secondapp.
  */

    tpsetctxt(secondapp_contextID, 0);
/*
    * You call services offered by secondapp.
* Then you switch back to firstapp.
*/
  tpsetctxt(firstapp_contextID, 0);
/*
    * You call services offered by firstapp. When you have
    * finished, you terminate the context for firstapp.
*/
tpterm();
/*
    * Then you switch back to secondapp.
*/
tpsetctxt(secondapp_contextID, 0);
/*
    * You call services offered by secondapp. When you have
        finished, you terminate the context for secondapp and
     end your program.
    */

    tpterm();

    return(0);

}

10.3.2.6 非請求メッセージの処理

非請求メッセージを処理する各コンテキストでは、非請求メッセージ・ハンドラを設定するか、またはプロセス・ハンドラのデフォルトが設定されている場合はそれを使用する必要があります。

tpsetunsol()がコンテキストに関連付けされていないスレッドから呼び出されると、新しく生成されるすべてのtpinit()コンテキストに対して、プロセス単位のデフォルトの非請求メッセージ・ハンドラが作成されます。特定のコンテキストは、コンテキストがアクティブのときにtpsetunsol()を再度呼び出して、そのコンテキストの非請求メッセージ・ハンドラを変更することができます。プロセス単位のデフォルトの非請求メッセージ・ハンドラは、コンテキストに現在関連付けされていないスレッドでtpsetunsol()を再度呼び出すと、変更できます。

ハンドラの設定は、シングル・スレッド・アプリケーションまたはシングル・コンテキスト・アプリケーションのハンドラを設定する場合と同じように行います。詳細は、tpsetunsol()に関する項を参照してください。

現在処理を行っているコンテキストを識別するには、非請求メッセージ・ハンドラ内でtpgetctxt(3c)を使用します。

10.3.2.7 マルチスレッド/マルチコンテキストATMIアプリケーションにおけるトランザクションのコーディング規則

アプリケーションをコーディングする場合、トランザクションの使用に関する次の事項に注意してください:

  • 1つのコンテキストで所有できるトランザクションは1つだけです。
  • 各コンテキストで異なるトランザクションを使用できます。
  • ある時点で任意のコンテキストに関連付けされているすべてのスレッドは、そのコンテキストの同じトランザクション状態を共有します。
  • スレッドを同期して、すべての会話とRPC呼出しが完了してからtpcommit()が呼び出されるようにします。
  • tpcommit()の呼出しは、特定のトランザクションの1つのスレッドからのみ行うことができます。

関連項目:

10.3.3 ATMIサーバーでサーバー・ディスパッチのマルチコンテキストおよびマルチスレッドのスレッドを使用するためのコーディング

このトピックには、次の項があります。

ノート: この項で示す手順とサンプル・コードは、Oracle Tuxedoシステムで提供されるC言語のライブラリ関数を参照します。(詳細は、『Oracle Tuxedo C関数リファレンス』を参照してください。)COBOLアプリケーションではマルチコンテキスト・サーバーの生成に必要なマルチスレッドがサポートされていないので、C言語の関数に相当するCOBOLルーチンは利用できません。

10.3.3.1 コンテキストの属性

コンテキストを使用する場合、コーディングで以下の事柄に注意してください。

  • 元のディスパッチ・スレッドが終了する前に、サーバー・ディスパッチ・コンテキストに現在関連付けられているアプリケーション生成のサーバー・スレッドがコンテキストを変更せずに終了すると、tpreturn()またはtpforward()が失敗します。スレッドを終了しても、自動的にtpsetctxt(3c)の呼出しがトリガーされてコンテキストがTPNULLCONTEXTに変更されることはありません。
  • プロセス内のすべてのコンテキストに、同じバッファ・タイプ・スイッチを使用します。
  • 他のタイプのデータ構造体と同じように、マルチスレッド・アプリケーションではOracle Tuxedoバッファを適切に使用する必要があります。つまり、次のいずれかが該当する場合、バッファを2つの呼出しで同時に使用することはできません:
    • 両方の呼出しでバッファを使用する場合
    • 両方の呼出しでバッファを解放する場合
    • 1つの呼出しでバッファを使用し、もう1つの呼出しでバッファを解放する場合

10.3.3.2 マルチコンテキストATMIサーバーでのサーバー・ディスパッチ・スレッドのコーディング規則

マルチコンテキスト・サーバー使用する場合、コーディングで以下の規則に注意してください。

  • サーバー上のOracle Tuxedoディスパッチャは、同じサービスまたは異なるサービスを複数回ディスパッチでき、ディスパッチされるサービスごとに異なるディスパッチ・コンテキストを生成します。
  • サーバーは、tpinit()を呼び出すことはできません。サーバー・プロセスでtpinit()が呼び出されると、tpinit()-1を返してtperrno(5)TPEPROTOに設定します。
  • サーバー・ディスパッチ・スレッドだけがtpreturn()またはtpforward()を呼び出すことができます。
  • アプリケーション生成のスレッドが任意のアプリケーションのコンテキストに関連付けされている場合、サーバーはtpreturn()またはtpforward()を実行できません。そのため、サーバー・ディスパッチ・スレッドがtpreturn()を呼び出す前に、そのコンテキストに対応するアプリケーション生成の各スレッドがコンテキストをTPNULLCONTEXTまたは別の有効なコンテキストに設定してtpsetctxt(3c)を呼び出す必要があります。

この規則に違反すると、tpreturn()またはtpforward()はユーザー・ログにメッセージを書き込み、呼出し側にTPESVCERRを示して、メイン・サーバーのディスパッチ・ループに制御を戻します。無効tpreturn()が実行されたコンテキスト内のスレッドは、無効コンテキスト状態になります。

  • tpreturn()またはtpforward()が呼び出された時点で未終了のATMI呼び出し、RPC呼び出し、または会話があると、tpreturn()またはtpforward()はユーザー・ログにメッセージを書き込み、呼出し側にTPESVCERRを示して、メイン・サーバーのディスパッチ・ループに制御を戻します。
  • サーバー・ディスパッチ・スレッドでtpsetctxt(3c)を呼び出すことはできません。
  • シングル・コンテキストのサーバーとは異なり、マルチコンテキスト・サーバー・スレッドでは、同じサーバー・プロセスだけで提供されるサービスを呼び出すことができます。

10.3.3.3 ATMIサーバーおよびATMIサーバー・スレッドの初期化と終了

サーバーとサーバー・スレッドの初期化と終了には、Oracle Tuxedoシステムで提供されるデフォルトの関数を使用できます。

表10-1 初期化と終了を行うデフォルトの関数

目的 使用するデフォルトの関数
サーバーの初期化 tpsvrinit (3c)
サーバー・スレッドの初期化 tpsvrthrinit (3c)
サーバーの終了 tpsvrdone (3c)
サーバー・スレッドの終了 tpsvrthrdone (3c)

関連項目: 10‑14ページの「ATMIサーバーでのサーバー・ディスパッチ・スレッドのマルチスレッドとマルチコンテキストの動作」

10.3.4 ATMIサーバーのアプリケーション生成スレッドでマルチコンテキストを使用するためのコーディング

このトピックには、次の項があります。

10.3.4.1 スレッドの生成

オペレーティング・システムのスレッド関数を使用して、Tuxedoアプリケーション・サーバーに新しいスレッドを生成できます。これらの新しいアプリケーションは、Tuxedoシステムから独立して動作できます。また、いずれかのサーバー・ディスパッチ・スレッドと同じコンテキストで動作することもできます。アプリケーション生成のサーバー・スレッドは、tpappthrinit(3c)によって生成された別のコンテキストで動作することもできます。

10.3.4.2 コンテキストへのアプリケーション・スレッドの関連付け

最初、アプリケーション生成サーバー・スレッドは、どのTuxedoコンテキストにも関連していません。ただし、初期化される前に呼び出された場合、ほとんどのATMI関数は暗黙的にtpinit()を実行します。サーバーでtpinit()を呼び出すことは禁止されているので、そのような呼出しを行うと問題が発生します。(サーバー・プロセスでtpinit()が呼び出されると、tpinit()-1を返してtperrno(5)TPEPROTOを設定します。)

そのため、アプリケーション生成のサーバー・スレッドは、有効なコンテキストと関連付けを行ってからATMI関数を呼び出す必要があります。アプリケーション生成のサーバー・スレッドは、次のように動作します。

  • 自身を既存のサーバー・ディスパッチ・コンテキストに関連付けます。
  • 自身をtpappthrinit(3c)によって生成された別のコンテキストに関連付けます(この機能は、Tuxedo 11gリリース1 (11.1.1.3.0)で利用できます)

10.3.4.3 既存のサーバー・ディスパッチ・コンテキストへのアプリケーション・スレッドの関連付け

アプリケーション生成のサーバー・スレッドを既存のサーバー・ディスパッチ・コンテキストに関連付けるには、以下の手順をコーディングします。

  1. Server-dispatched-thread_Aは、tpgetctxt(3c)を呼び出して現在のコンテキストへのハンドルを取得します。
  2. Server-dispatched-thread_Aは、tpgetctxt(3c)が返すハンドルをApplication_thread_Bに渡します。
  3. Application_thread_Bは、tpsetctxt(3c)を呼び出してServer-dispatched-thread_Aから受け取ったハンドルを指定して、現在のコンテキストとの関連付けを作成します。

ノート:

アプリケーション生成のサーバー・スレッドは、tpreturn()またはtpforward() (あるいはその両方)を呼び出すことはできません。元のサーバー・ディスパッチ・スレッドがtpreturn()またはtpforward()を呼び出す前に、そのコンテキストにあったすべてのアプリケーション生成のサーバー・スレッドはTPNULLCONTEXTまたは別の有効なコンテキストに切り替える必要があります。

この規則に違反すると、tpforward()またはtpreturn()が失敗し、呼出し側にサービス・エラーが示されます。

10.3.4.4 マルチコンテキスト・サーバーでの既存のサーバー・ディスパッチ・コンテキストにアプリケーション・スレッドを関連付けするためのサンプル・コード

サーバーでアプリケーション・スレッドを生成する必要があるアプリケーションの場合、次のリストは、サービスが別のスレッドを生成して同じサーバー・ディスパッチ・コンテキストでそのサービスの作業を行うマルチコンテキスト・サーバーを示しています。オペレーティング・システムのスレッド関数は、オペレーティング・システムによって異なります。このサンプル・コードでは、POSIX関数とATMI関数が使用されています。

ノート:

コードを簡単にするために、エラー・チェックは省略してあります。また、Oracle Tuxedoシステムによってディスパッチされたスレッドだけを使用するマルチコンテキスト・サーバーも省略してあります。そのようなサーバーのコーディングは、スレッド・セーフのプログラミング方法が使用されている場合、シングル・コンテキスト・サーバーのコーディングとまったく同じです。

アプリケーション生成のサーバー・スレッドがサーバー・ディスパッチ・コンテキストで動作するサンプル・コードのリスト

#include <pthread.h>
#include <atmi.h>

void *withdrawalthread(void *);

struct sdata {
   TPCONTEXT_T ctxt;
   TPSVCINFO   *svcinfoptr;
};

void
TRANSFER(TPSVCINFO *svcinfo)
{
     struct sdata transferdata;
     pthread_t withdrawalthreadid;

     tpgetctxt(&transferdata.ctxt, 0);
     transferdata.svcinfoptr = svcinfo;
pthread_create( &withdrawalthreadid, NULL,
withdrawalthread, &transferdata );
    tpcall("DEPOSIT", ...);
    pthread_join(withdrawalthreadid, NULL);
    tpreturn(TPSUCCESS, ...);
}

void *
withdrawalthread(void *arg)
{
    tpsetctxt(arg->ctxt, 0);
    tpopen();
    tpcall("WITHDRAWAL", ...);
    tpclose();
    tpsetctxt(TPNULLCONTEXT, 0);
    return(NULL)
}

10.3.4.5 アプリケーション生成のコンテキストへのアプリケーション・スレッドの関連付け

ATMIサーバー内のアプリケーション生成のATMIサーバー・スレッドは別のTuxedoコンテキストを生成し、tpappthrinit(3c)を呼び出してこのコンテキストに自身を関連付けることができます。

10.3.4.5.1 コンテキストの属性
  • tpappthrinit(3c)で作成されるコンテキストは、サーバー・ディスパッチ・コンテキストには依存していません。アプリケーション・サーバーがあるドメインに関連します。
  • tpappthrinit(3c)によって生成されたコンテキストは、アプリケーション生成のサーバー・スレッドがそのコンテキストでの処理を終了した後に、tpappthrterm(3c)を呼び出して終了する必要があります。
  • アプリケーションで使用されるセキュリティ・メカニズムのタイプによっては、tpappthrinit(3c)を呼び出す際に、ユーザー認証情報をコンテキストと関連付けることができます。アプリケーション生成コンテキストの各アプリケーションとの関連付けには、別個にセキュリティ検証を行う必要があります。
10.3.4.5.2 アプリケーション生成のコンテキストでのATMIサーバーのアプリケーション生成スレッドのコーディング規則
  • tpappthrinit()/tpappthrterm()を呼び出すことができるのは、Tuxedoサーバー・プロセスのみです。
  • buildserverコマンドを実行する場合、"-t"オプションを指定してサーバーをビルドする必要があります。
  • クライアント・プログラムでtpappthrinit()/tpappthrterm()を呼び出すことはできません。
  • サーバー・ディスパッチ・スレッドでtpappthrinit()/tpappthrterm()を呼び出すことはできません。
  • サーバー・ディスパッチ・コンテキストに現在関連付けられているアプリケーション生成スレッドでtpappthrterm()を呼び出すことはできません。
  • アプリケーション生成のコンテキストで他のアプリケーション・スレッドがまだ処理を行っている場合は、そのコンテキストでtpappthrterm()を呼び出さないようにする必要があります。
  • ハンドルと呼出し記述子は、同じプロセスの同じコンテキスト内で移植できます。ただし、ほかのコンテキストやプロセスには移植できません。ハンドルと呼出し記述子は、それらが元々割り当てられていたアプリケーション・コンテキストだけで使用できます。
  • 会話が開始されると、同じプロセスの同じコンテキストに存在するすべてのスレッドでその会話を操作できます。
  • 同じプロセスの同じコンテキストで動作するアプリケーション・スレッドの場合、そのアプリケーション・スレッドがtpacall()の呼出し側かどうかに関係なく、tpgetrply()を呼び出して以前のtpacall()呼出しのレスポンスを受け取ることができます。
  • トランザクションをコミットまたは中断できるのは、同じプロセスの同じコンテキストに存在する1つのアプリケーション・スレッドだけです。トランザクションを開始したアプリケーション・スレッドである必要はありません。
  • すでにトランザクション・モードにあるコンテキストにtpbegin()を呼び出すことはできません。
  • XAトランザクションで構成されているグループにアプリケーション・サーバーが属している場合、tpopen()を呼び出してから、tpbegin()やSQL操作などのトランザクション・アクティビティを実行する必要があります。トランザクション操作を終了する場合は、tpclose()を呼び出す必要があります。
  • アプリケーション生成のサーバー・スレッドは、非請求メッセージを送信することはできますが、受信することはできません。

10.3.4.6 マルチコンテキスト・サーバーでのアプリケーション生成のサーバー・コンテキストにアプリケーション・スレッドを関連付けするためのサンプル・コード

次のリストは、サービスが別のスレッドを生成するマルチコンテキスト・サーバーを示しています。このコードは、サーバーでアプリケーション・スレッドを生成する必要があるアプリケーションで使用します。このアプリケーション生成のサーバー・スレッドは別のコンテキストで動作します。オペレーティング・システムのスレッド関数は、オペレーティング・システムによって異なります。このサンプル・コードでは、POSIX関数とATMI関数が使用されています。

ノート:

コードを簡単にするために、エラー・チェックは省略してあります。

アプリケーション生成のサーバー・スレッドがアプリケーション生成のコンテキストで動作するサンプル・コードのリスト

#include <pthread.h>
#include <atmi.h>

void *withdrawalthread(void *);

void
TRANSFER(TPSVCINFO *svcinfo)
{
    pthread_t withdrawalthreadid;

    pthread_create( &withdrawalthreadid, NULL,
                     withdrawalthread, … );
    tpcall("DEPOSIT", ...);

    pthread_join(withdrawalthreadid, NULL);
    tpreturn(TPSUCCESS, ...);
}

void *
withdrawalthread(void *arg)
{

    tpappthrinit(NULL);
    tpopen();
    tpcall("WITHDRAWAL", ...);
    tpclose();
    tpappthrterm();
    return(NULL);
}

10.3.5 マルチスレッドATMIクライアントのコーディング

このトピックには、次の項があります。

ノート:

Oracle Tuxedoシステムでは、COBOLアプリケーションのマルチスレッドはサポートされていません。

10.3.5.1 マルチスレッドATMIクライアントのコーディング規則

マルチスレッド・クライアントを使用する場合、コーディングで以下の規則に注意してください。

  • 会話が開始されると、同じプロセス内のすべてのスレッドでその会話を操作できます。ハンドルと呼出し記述子は、同じプロセスの同じコンテキスト内で移植できます。ただし、ほかのコンテキストやプロセスには移植できません。ハンドルと呼出し記述子は、それらが元々割り当てられていたアプリケーション・コンテキストだけで使用できます。
  • 同じプロセスの同じコンテキストで動作するスレッドの場合、そのスレッドがtpacall()の呼出し側かどうかに関係なく、tpgetrply()を呼び出して以前のtpacall()呼出しのレスポンスを受け取ることができます。
  • トランザクションをコミットまたは中断できるのは、1つのスレッドだけです。トランザクションを開始したスレッドである必要はありません。
  • すべてのRPC呼出しとすべての会話は、トランザクションをコミットする前に完了していなければなりません。未終了のRPC呼出しや会話があるときにtpcommit()が呼び出されると、tpcommit()はトランザクションを中断し、-1を返してtperrno(5)TPEABORTを設定します。
  • トランザクションがまだコミットまたは中断していないことを確認できない場合に、tpcall()tpacall()tpgetrply()tpconnect()tpsend()tprecv()およびtpdiscon()などの関数をトランザクション・モードで呼び出すことはできません。
  • 同じコンテキストに2つのtpbegin()呼出しを同時に行うことはできません。
  • すでにトランザクション・モードにあるコンテキストにtpbegin()を呼び出すことはできません。
  • クライアントを使用している場合に複数のドメインに接続するときは、TUXCONFIGまたはWSNADDRの値を手動で変更してからtpinit()を呼び出します。このような処理が複数のスレッドで行われる場合、環境変数の設定とtpinit()呼出しを同期する必要があります。クライアントのすべてのアプリケーション関連付けは、以下の規則に従う必要があります。
    • すべての関連付けは、同じリリースのOracle Tuxedoシステムに対して行われます。
    • 特定のクライアントのすべてのアプリケーション関連付けはネイティブ・クライアントとして生成するか、またはワークステーション・クライアントとして生成します。
  • アプリケーションに参加するには、マルチスレッドのワークステーション・クライアントが、シングル・コンテキスト・モードで実行している場合でも、常にTPMULTICONTEXTSフラグを設定してtpinit()関数を呼び出す必要があります。

10.3.5.2 ATMIクライアントの複数のコンテキストへの初期化

クライアントを複数のコンテキストに参加させるには、TPINITデータ構造体のflags要素にTPMULTICONTEXTSフラグを設定してtpinit()関数を呼び出します。

1つのプロセスでは、tpinit()へのすべての呼出しにTPMULTICONTEXTSフラグを含めるか、またはtpinit()へのすべての呼出しにこのフラグを含めません。この規則には、例外が1つあります。つまり、tpterm()への呼出しが正常に終了して、クライアントのすべてのアプリケーション関連付けが終了した場合、次にtpinit()を呼び出すときに必ずしもTPMULTICONTEXTSフラグを含む必要のない状態にプロセスが復元されます。

TPMULTICONTEXTSフラグを設定してtpinit()関数が呼び出されると、アプリケーションとの新しい関連付けが生成され、スレッドに対するカレントの関連付けが指定されます。新しい関連付けが生成されるOracle Tuxedoドメインは、TUXCONFIGまたはWSENVFILE/WSNADDR環境変数の値で決定されます。

クライアント・スレッドがTPMULTICONTEXTSフラグを設定せずにtpinit()を正常に実行した場合、クライアントのすべてのスレッドがシングル・コンテキスト状態(TPSINGLECONTEXT)になります。

tpinit()が失敗した場合、呼出し側スレッドは元のコンテキスト、つまりtpinit()呼出しの前に操作していたコンテキスト状態のままになります。

まだ動作中のスレッドがあるコンテキストからtpterm()を呼び出すことはできません。このような状況やそれ以外の状況でtpterm()を呼び出した結果生じるコンテキスト状態については、「マルチコンテキスト状態の遷移」を参照してください。

10.3.5.3 ATMIクライアント・スレッドのコンテキスト状態の変化

マルチコンテキストのアプリケーションでは、いろいろな関数を呼び出すと、呼出し側スレッド、および呼出し側プロセスと同じコンテキストでアクティブなその他のスレッドのコンテキスト状態が変化します。次の図は、tpinit()tpsetctxt(3c)およびtpterm()を呼び出した結果、変化したコンテキスト状態を示しています。(tpgetrply()関数を呼び出しても、コンテキスト状態は変化しません。)

図10-4 マルチコンテキスト状態の遷移


マルチコンテキスト状態の遷移の図

ノート:

tpterm()がマルチコンテキスト状態(TPMULTICONTEXTS)で実行しているスレッドによって呼び出されると、呼出し側スレッドはNULLコンテキスト状態(TPNULLCONTEXT)になります。終了するコンテキストに関連するその他すべてのスレッドは、無効コンテキスト状態(TPINVALIDCONTEXT)に切り替わります。

次の表は、tpinit()tpsetctxt(3c)およびtpterm()を呼び出した結果、変化したコンテキスト状態を示しています。

表10-2 クライアント・スレッドのコンテキスト状態の変化

この関数が実行されるとき このコンテキスト状態のスレッドの結果は...
NULLコンテキスト シングル・コンテキスト マルチコンテキスト 無効コンテキスト
TPMULTICONTEXTSが設定されていないtpinit() シングル・コンテキスト シングル・コンテキスト エラー エラー
TPMULTICONTEXTSが設定されたtpinit() マルチコンテキスト エラー マルチコンテキスト エラー
TPNULLCONTEXTtpsetctxt(3c) NULL エラー NULL NULL
context 0tpsetctxt(3c) エラー シングル・コンテキスト エラー エラー
context > 0tpsetctxt(3c) マルチコンテキスト エラー マルチコンテキスト マルチコンテキスト
暗黙的なtpinit() シングル・コンテキスト なし なし エラー
このスレッドでのtpterm() NULL NULL NULL NULL
このコンテキストの異なるスレッドでのtpterm() なし NULL 無効 なし

10.3.5.4 マルチスレッド環境での応答の取得

tpgetrply()は、tpacall()からのリクエストに対するレスポンスだけを受け取ります。tpcall()からのリクエストは、マルチスレッドまたはマルチコンテキストのレベルに関係なく、tpgetrply()で取得することはできません。

tpgetrply()は、1つのコンテキスト、つまりtpgetrply()の呼出し側コンテキストだけで動作します。そのため、TPGETANYフラグを設定してtpgetrply()を呼び出すと、同じコンテキストで生成されたハンドルだけが考慮されます。同じように、あるコンテキストで生成されたハンドルを別のコンテキストで使用することはできません。ただし、同じコンテキストで動作するスレッドには、そのハンドルを使用できます。

tpgetrply()をマルチスレッド環境で呼び出す場合、次の制約があります:

  • 1つのスレッドがすでにtpgetrply()で特定のハンドルを待機しているときに、同じコンテキストの別のスレッドがtpgetrply()を呼び出して同じハンドルの取得を試みると、tpgetrply()-1を返し、tperrnoTPEPROTOを設定します。
  • 1つのスレッドがすでにTPGETANYフラグを設定してtpgetrply()の応答を待機しているときに、同じコンテキストの別のスレッドがtpgetrply()を呼び出して特定のハンドルの取得を試みると、tpgetrply()は-1を返し、tperrno(5)TPEPROTOを設定します。

    これは、1つのスレッドがすでにtpgetrply()で特定のハンドルを待機しているときに、同じコンテキストの別のスレッドがTPGETANYフラグを設定してtpgetrply()を呼び出した場合も同じです。これらの制約により、特定のハンドルを待機しているスレッドがある場合、その応答が別のスレッドに渡されることがなくなります。

  • ある時点で、TPGETANYフラグを設定してtpgetrply()の応答を待機できるのは、特定のコンテキスト内で1つのスレッドだけです。TPGETANYフラグを設定して呼び出したtpgetrply()がまだ処理されていない場合に、同じコンテキストの別のスレッドが同じ呼出しを行うと、この2番目の呼出しは-1を返し、tperrno(5)TPEPROTOに設定します。

10.3.5.5 マルチスレッド・マルチコンテキスト環境の環境変数の使用

Oracle Tuxedoアプリケーションをマルチコンテキスト・マルチスレッド環境で実行する場合、環境変数に関して以下の事柄に注意してください。

  • プロセスは、初期状態ではその環境をオペレーティング・システム環境から継承します。環境変数がサポートされているプラットフォームでは、そのような変数はプロセス単位で動作するエンティティを構成します。そのため、コンテキスト単位の環境設定に依存するアプリケーションでは、オペレーティング・システム関数ではなくtuxgetenv(3c)関数を使用する必要があります。

    ノート:

    オペレーティング・システム環境が認識されないオペレーティング・システムの場合、初期状態では空の環境になっています。
  • 多くの環境変数は、Oracle Tuxedoシステムでプロセスごとに1回、またはコンテキストごとに1回だけ読み取られ、Oracle Tuxedoシステム内にキャッシュされます。プロセスに一度キャッシュされた変数を変更しても影響はありません。
キャッシュ処理は次で行われます... 次の環境変数に対して...
コンテキスト単位 TUXCONFIG
FIELDTBLSおよびFIELDTBLS32
FLDTBLDIRおよびFLDTBLDIR32
ULOGPFX
VIEWDIRおよびVIEWDIR32
WSNADDR
WSDEVICE
WSENV
プロセス単位 TMTRACE
TUXDIR
ULOGDEBUG
  • tuxputenv(3c)関数はプロセス全体の環境に影響します。
  • tuxreadenv(3c)関数を呼び出すと、環境変数を含むファイルが読み取られ、それらの変数がプロセス全体の環境に追加されます。
  • tuxgetenv(3c)関数は、現在のコンテキストで要求された環境変数の現在の値を返します。初期設定では、すべてのコンテキストが同じ環境になります。ただし、特定のコンテキストに固有の環境ファイルを使用すると、コンテキストごとに異なる環境設定を持つことができます。
  • クライアントが複数のドメインに初期化する場合、tpinit()を呼び出す前に、毎回TUXCONFIGWSNADDRまたはWSENVFILE環境変数の値を適切な値に変更する必要があります。そのようなアプリケーションがマルチスレッドの場合、以下の処理を確実に行うために、ミューテックスなどのアプリケーション定義の同時実行性制御が必要になります。
    • 適切な環境変数が再設定されること
    • 他のスレッドによって環境変数が再設定されずにtpinit()が呼び出されること
  • クライアントがシステムに初期化する場合、WSENVFILEやマシン環境ファイルが読み取られ、そのコンテキストの環境だけが影響を受けます。環境ファイルでオーバーライドされないコンテキスト部分には、プロセス全体に対する以前の環境が適用されます。

10.3.6 マルチスレッドATMIクライアントでのコンテキスト単位の関数とデータ構造体

以下に示すATMI関数は、呼出し側のアプリケーション・コンテキストだけに影響します。

ノート:

tpbroadcast()の場合、ブロードキャスト・メッセージは特定のアプリケーションとの関連付けから送られたものとして識別されます。tpnotify(3c)の場合、通知は特定のアプリケーションとの関連付けから送られたものとして識別されます。tpinit()のノートについては、「マルチスレッド・クライアントでのプロセス単位の関数とデータ構造体」を参照してください。

tpsetunsol()がコンテキストに関連付けされていないスレッドから呼び出されると、新しく生成されるすべてのtpinit()コンテキストに対して、プロセス単位のデフォルトの非請求メッセージ・ハンドラが作成されます。特定のコンテキストは、コンテキストがアクティブのときにtpsetunsol()を再度呼び出して、そのコンテキストの非請求メッセージ・ハンドラを変更することができます。プロセス単位のデフォルトの非請求メッセージ・ハンドラは、コンテキストに現在関連付けされていないスレッドでtpsetunsol()を再度呼び出すと、変更できます。

  • CLIENTID、クライアント名、ユーザー名、トランザクションID、およびTPSVCINFOデータ構造体の内容は、同じプロセス内のコンテキストによって異なる場合があります。
  • 非同期呼出しハンドルと接続記述子は、その生成元コンテキスト内で有効です。任意通知のタイプは、コンテキストごとに固有です。シグナル・ベースの通知はマルチコンテキストでは使用できませんが、各コンテキストでは次のいずれかのオプションを使用できます。
    • 非請求メッセージの無視
    • ディップ・イン通知の使用
    • 専用のスレッド通知の使用

10.3.7 マルチスレッドATMIクライアントでのプロセス単位の関数とデータ構造体

以下に示すOracle Tuxedo関数は、呼出し側のプロセス全体に影響します。

シングル・コンテキスト・モード、マルチコンテキスト・モード、または非初期化モードのどれを使用するかは、プロセス全体に影響します。また、バッファ・タイプ・スイッチ、ビュー・キャッシュ、および環境変数の値も、プロセス単位の関数です。

10.3.8 マルチスレッドATMIクライアントでのスレッド単位の関数とデータ構造体

以下に示す関数は、呼出し側のスレッドだけに影響します。

FerrorFerror32(5)tperrno(5)tpurcode(5)およびUunix_err変数は、各スレッドに固有です。詳細は、「セクション5 - ファイル形式、データ記述、MIBおよびシステム・プロセス・リファレンス」を参照してください。

現在のコンテキストのIDは各スレッドに固有です。

10.3.9 マルチスレッドATMIクライアントのサンプル・コード

次のリストは、ATMI呼出しを使用するマルチスレッド・クライアントを示しています。スレッド関数は、オペレーティング・システムによって異なります。この例では、POSIX関数が使用されています。

コードを簡単にするために、エラー・チェックは省略してあります。

マルチスレッド・クライアントのサンプル・コードのリスト

#include <stdio.h>
#include <pthread.h>
#include <atmi.h>

TPINIT   *   tpinitbuf;
int          timeout=60;
pthread_t    withdrawalthreadid, stockthreadid;
TPCONTEXT_T ctxt;
void * stackthread(void *);
void * withdrawalthread(void *);

main()
{

      tpinitbuf = tpalloc(TPINIT, NULL, TPINITNEED(0));
      /*
       * This code will perform a transfer, using separate threads for the                                       
       * withdrawal and deposit. It will also get the current 
       * price of Oracle stock from a separate application, and calculate how
       * many shares the transferred amount can buy.
       */

      tpinitbuf->flags = TPMULTICONTEXTS;

      /* Fill in the rest of tpinitbuf.   */
      tpinit(tpinitbuf);
      tpgetctxt(&ctxt, 0);  
      tpbegin(timeout, 0);
      pthread_create(&withdrawalthreadid, NULL, withdrawalthread, NULL);
      tpcall("DEPOSIT", ...);

      /* Wait for the withdrawal thread to complete. */
      pthread_join(withdrawalthreadid, NULL);

      tpcommit(0);
      tpterm();

      /* Wait for the stock thread to complete. */
      pthread_join(stockthreadid, NULL);

      /* Print the results. */
      printf("$%9.2f has been transferred \
      from your savings account to your checking account.\n", ...);

      printf("At the current Oracle stock price of $%8.3f, \
      you could purchase %d shares.\n", ...);

      exit(0);
}
void *
stockthread(void *arg)
{

      /* The other threads have now called tpinit(), so resetting TUXCONFIG can
       * no longer adversely affect them.
       */

      tuxputenv("TUXCONFIG=/home/users/xyz/stockconf");
      tpinitbuf->flags = TPMULTICONTEXTS;  
      /* Fill in the rest of tpinitbuf. */
      tpinit(tpinitbuf);
      tpcall("GETSTOCKPRICE", ...);
      /* Save the stock price in a variable that can also be accessed in main(). */
      tpterm();
      return(NULL);
}


void *
withdrawalthread(void *arg)
{
      /* Create a separate thread to get stock prices from a different
       * application. 
       */


      pthread_create(&stockthreadid, NULL, stockthread, NULL);
      tpsetctxt(ctxt, 0);
      tpcall("WITHDRAWAL", ...);
      return(NULL);
}

10.3.10 マルチスレッドATMIサーバーのコーディング

ほとんどの場合、マルチスレッド・サーバーはマルチコンテキストでもあります。マルチスレッドATMIサーバーのコーディングについては、「ATMIサーバーでサーバー・ディスパッチのマルチコンテキストおよびマルチスレッドのスレッドを使用するためのコーディング」を参照してください。

10.3.11 マルチスレッド/マルチコンテキストATMIアプリケーションのコードのコンパイル

buildserver(1)buildclient(1)など、コンパイルまたはビルドの実行可能ファイル用にOracle Tuxedoシステムで提供されるプログラムには、必要なコンパイラ・フラグが自動的に設定されます。これらのツールを使用すると、コンパイル時にフラグを設定する必要がありません。

ただし、最終的なコンパイルの前に.cファイルを.oファイルにコンパイルする場合は、プラットフォーム固有のコンパイラ・フラグを設定する必要があります。そのようなフラグは、単一のプロセスにリンクするすべてのコードに一貫して設定しなければなりません。

マルチスレッド・サーバーを生成する場合、-tオプションを指定してbuildserver(1)コマンドを実行する必要があります。これはマルチスレッド・サーバーの場合に必須のオプションです。ビルド時にこのオプションが指定されておらず、その後、MAXDISPATCHTHREADSの値が1を超える構成ファイルを使用して新しいサーバーを起動すると、警告メッセージがユーザー・ログに記録され、サーバーはシングル・スレッドの動作に戻ります。

マルチスレッド環境で.cファイルを.oファイルにコンパイルする場合に必要なオペレーティング・システム固有のコンパイラ・パラメータを識別するには、-vオプションを指定してbuildclient(1)またはbuildserver(1)をテスト・ファイルで実行します。