The ADAPTIVE Communication Environment

An Object-Oriented Network Programming Toolkit
for Developing Communication Software

Douglas C. Schmidt


概要

ACE(ADAPTIVE Communication Environment)はコンカレントなイベント駆動通信システムの開発を 簡略化する基本的なデザインパターンである。ACEはイベントの処理、イベントハンドラの起動、 コネクションの確立、ルーティング、アプリケーションサービスの動的コンフィグレーション、 並列性の制御といった通信ソフトウェア共通の処理を行なう多くのOSの上で動作可能な 再利用可能なC++ラッパー、フレームワークとクラスカテゴリの豊富なセットを提供する。

ACEの主な目的は、OSメカニズムの利用方法の単純化、プロセス間通信の提供、通信ポートの処理、 明示的な動的リンクと並列性である。加えてACEは、実行中のアプリケーションにサービスを動的に リンクし、それを複数のプロセスあるいはスレッドで実行させることで、通信ソフトのコンフィグ レーション・リコンフィグレーションを自動化する。

この論文ではACEの構造と機能、ACEの特徴を説明するためのいくつかの例を示す。ACEはフリー で提供され、以下のようなところで商用目的にに使われている。

また、同様に多くの研究機関や企業の研究プロジェクトでも使用されてる。ACEはSunOS 4.x,5x , SGI, HP-UX, OSF/1, AIXそしてWindowsNTなどの多くのシングルプロセッサあるいはマルチプロセッサのOSプラットフォームに移植されている。

1. はじめに

1.1 動機

障害に強く、高性能な分散通信システムソフトウェアシステムへの需要が近年劇的に増加している。こうしたシステム例としては、広域個人通信システム、通信管理のプラットフォーム、先進的な医療イメージシステムそしてリアルタイム市場データモニタ・分析システムがある。分散コンピューティングは接続性と内部動作(並列処理によるパフォーマンス、複製による信頼性と高可用性、モジュール化によるスケーラビリティとポータビリティ、動的構成と再構成による拡張性、資源の共有とオープンシステムによるコストパフォーマンス)を通し協調作業の完全を約束する技術である。

独立したコンピュータのネットワーク全体の分散サービスは多くの潜在的な利益を提供するが、分散コミュニケーションソフトウェアの開発はスタンドアロンアプリケーションに比べ複雑である。この複雑さの多くは、コミュニケーションソフトを開発するための使い古された道具と技術という制限から来ている。特に、多くの標準ネットワーク機構(sockets, TLI等)と再利用部品ライブラリ(X window, Sun RPC等)は型安全、移植性、リエントラント性、アプリケーションプログラミングインタフェース(API)の拡張性に欠ける。例えば、socketsとTLIは通信の両端を弱い型付けの整数のハンドルで識別しており、これは分かり難い実行時のエラーの可能性を増加させる。

開発を難しくしているもうひとつの要因は、アルゴリズムによる分割の普及である。多くの分散アプリケーションは、拡張性がないシステムアーキテクチャとよく言われるアルゴリズムによる分割技術を用いて開発されている。人気のあるネットワークプログラミングのテキストブックのソースコード例がアルゴリズム主導の設計・実装技術に基づいているということが、より問題を悪化させている。

オブジェクト指向技術はコミュニケーションシステムの開発の複雑さを減らすことを助ける多くの原理、方法、ツールを提供する。実際にどれだけオブジェクト指向技術がうまく適用できるかを説明するために、本論文ではADAOTIVE Communication Environment(ACE)の構造と機能を見ていく。ACEは基本的な通信ソフトウェアのデザインパターンに基づいた、無償のオブジェクト指向ネットワークプログラミングツールキットである。

1.2 ACEツールキットの構造

ACEツールキットは低いレベルのC++ラッパー(OSの機構を隠蔽する)と高いレベルのクラスカテゴリとフレームワーク(これはC++ラッパーを拡張し、戦略的・戦術的な通信ソフトウェアデザインパターンを実装する)の両方を提供する層構造のアーキテクチャにより設計されている。

1.2.1 C++ラッパー

ACEはUNIXとWindowsNTの、以下のOSの並列性、プロセス間通信(IPC)、仮想メモリメカニズム(図1の一番下の部分)を隠蔽し拡張する、オブジェクト指向C++ラッパーを提供する。

アプリケーションは2章で説明するC++ラッパークラスライブラリを選択的に継承、集約、実体化し、これらの部品を組み合わせ構成する。C++ラッパーはC++の型安全のインタフェースでOSの通信、スレッド、仮想メモリ機構を隠蔽化することでアプリケーションの強度を高める。これによりアプリケーションが型安全ではないCライブラリに直接アクセスする量を減らすことができる。このためC++コンパイラは実行時ではなくコンパイル時に型不正を見つけることができる。ACEはC++ラッパーが提供する型安全と抽象化によるパフォーマンス低下を防ぐために、C++のinlineを使っている。

1.2.2. クラスカテゴリとフレームワーク

C++ラッパーに加え、ACEは低いレベルのC++ラッパーをてこ入れ拡張する高いレベルのフレームワークのコレクション(図1の上方)を提供する。フレームワークは関連するアプリケーション・ファミリーに再利用可能なアーキテクチャを形作る統合された部品の集合である。オブジェクト指向フレームワークは、グラフィカルユーザインタフェース、データベース、OSカーネル、通信サブシステムといった複雑なアプリケーション・ドメインで一般的になってきている。フレームワークは個々のクラスあるいは単独の関数の再利用にくらべ、ソフトウェアの大きな単位での再利用を可能にする。

ADAPTIVE Service eXecutive(ASX)フレームワーク(2.5章で説明)は複数の通信サービスからなる並列的な通信ソフトウェアアプリケーションの開発、構成、再構成を単純化・自動化する。ASXフレームワークは再利用可能なオブジェクト指向アーキテクチャにおいて、C++ラッパーに以下のものを提供できるようにする。

ASXは、テンプレート、継承、動的束縛等のC++言語の特徴とデザインパターンにより、変更・再コンパイル・再リンク・システムの再起動なしにアップデート・拡張できる通信ソフトウェアの開発を用意にする。

本論文は、2章でACE C++ラッパーと高レベルACEフレームワークを説明し、3章ではいくつかの製品アプリケーションのアーキテクチャの概要を、4章ではまとめを述べる。

2 ACE C++ラッパー

ACEの最下層は、OSのマルチスレッド、ローカル・リモートプロセス間通信(IPC)、仮想メモリ機構を隠蔽するC++ラッパーから成る。C++ラッパーは強く、コンパクト、ポータブル、拡張性のある通信ソフトウェアの設計と実装を容易にする。ACEラッパーは図2に示すクラス分類に分割できる。それぞれのクラス分類は、インタフェースとサービスの一まとまりのセットを提供する。ACEの主要なクラス分類を次に示す。

本章ではこれらそれぞれのクラス分類を議論する。本論文を通しACEの部品をBooch法で表現する。実線の長方形は複数の関連するクラスを共通の名前空間にいれるクラス分類を示す。実線の雲型はオブジェクトを示し、ネストは一方のオブジェクトが他方の成分であることを示し、矢印ではない線は2つのオブジェクト間にある種の関係があることを示す。点線の雲型はクラスを示し、矢印はクラス間の継承関係を示し、一端に小さな円がある線は2つのクラス間の成分あるいは利用関係を示す。3角形の中の文字Aはクラスが抽象クラスであることを示す。

2.1 Reactor: イベントの非多重化とサービスディスパッチ

通信ソフトウェアは多くの種類のイベント(タイマーベース、I/Oベース、シグナルベース等)を非多重化し、処理する。例えば、inetdスーパーサーバは一連のI/O通信ポートをモニターするイベントループを内部に持っている。それぞれの通信ポートは、ネットワークサービス(ftp, telnet, time-of-day, echo他)を提供するアプリケーション特化のハンドラと対応付けられている。クライアントからの要求がモニタしているポート(標準のftpサービスはポート21)に到着すると、要求を実行するために関連付けられたサービスハンドラにディスパッチされる。(例:inetdはクライアントに代わってファイルを転送するためにftpプロセスをforkしexecする)

これらのイベント処理の動作を一つにまとめ自動化するために、ACEはReactorと呼ばれるイベントの非多重化とイベントハンドラへのディスパッチのフレームワークを提供する。ReactorはUNIXとWindowsNTのイベント非多重化機構(select, poll, WaitForMultipleObjects等)をポータブルで拡張可能なC++ラッパーの中に隠蔽化している。これらのOSのイベント非多重化システムコールは複数のI/Oハンドルで同時に発生する複数の異なるタイプの入出力イベントを検出する。

アプリケーションのポータビリティーのため、Reactorはどのイベント非多重化機構が使われているか気にしないですむ一つのインタフェースを提供している。それに加えて、Reactorはマルチスレッドイベント処理環境でコールバックスタイルのディスパッチを正確かつ効率的に行うための相互排他機構を提供している。

Reactorの中のオブジェクトの構造を図3に示す。これらのオブジェクトは(1)イベントの非多重化(タイマー起動の呼び出しキューにより生成される一時的なイベント、通信ポートに到着するI/Oイベント、シグナルイベント等)と(2)これらのイベントを処理するための、事前に登録されたハンドラの適当なメソッドへのディスパッチを行う責任を持つ。図3に示すように、全てのイベントハンドラオブジェクトは抽象ベースクラスEvent Handlerから導出される。このクラスはReactorがイベントの到着に対応して適当なアプリケーション特化のメソッドへディスパッチに使われるインタフェースを定義する。

Reactorは、I/Oハンドルベース、タイマーベース、シグナルベースのイベントを非多重化するために、Event Handlerインタフェースで宣言される仮想メソッドを用いる。I/Oハンドルベースのイベントはhandle_input, handle_output, handle_exceptionsメソッドを通しディスパッチされ、タイマーベースのイベントは、handle_timeoutメソッドによりディスパッチされ、シグナルベースのイベントはhandle_signalメソッドでディスパッチされる。

Event Handlerのサブクラス(3.1章で述べるLogging Handler等)はベースクラスのインタフェースをメソッドとデータメンバーを追加することで増やすことができる。加えて、Event Handlerのインタフェースの仮想メソッドはサブクラスでアプリケーション特化の機能を実装するために選択的に重複定義することができる。例えば3.2章のPBX監視サーバのアプリケーション特化のサブクラスはクライアントと通信するEvent Handlerオブジェクトを継承と(または)2.2章で説明するSOCK SAPまたはTLI SAP転送インタフェースクラスのオブジェクトの具体化することで定義している。Event Handlerベースクラスの仮想メソッドをサブクラスで定義した後、アプリケーションはその結果のイベントハンドラオブジェクトを具体化する。

次の例は2つのプロセスの間で双方向通信チャネルを利用しメッセージを相互に交換し続ける簡単なプログラムである。この例は、どのようにサービスがEvent Handlerから継承されるかを示している。また、どのようにReactorを非多重化とI/Oベース、シグナルベース、タイマーベースのイベントをディスパッチするために用いるかも示している。以下に示すPing PongクラスはEvent Hnadlerクラスからインタフェースを継承し、アプリケーション特化の機能を実装している。

class Ping_Pong : public Event_Hander
{
public:
        Ping_Pong (char *b)
                : len (min (strlen (b) + 1, BUFSIZ)) {
                strncpy (this->buf, b, BUFSIZ);
        }
        virtual int handle_input (int fd) {
                return read (fd, this->buf, BUFSIZ);
        }
        virtual int handle_output (int fd) {
                return write (fd, this->buf, this->len);
        }
        virtual int handle_signal (int signum) {
                this->finished = 1;
        }
        virtual int handle_timeout (const Time_Value &,
                                        const void *) {
                this->finished = 1;
        }
        bool done (void) {
                return this->finished == 1;
        }
private:
        sig_atomic_t    finished;
        char            buf[BUFSIZ]
        size_t          len;
};





双方向の通信チャネルはSVR4 UNIX STREAM パイプによりつくられる。

static int
init_fds (int fds[])
{
        if (pipe (fds) == -1)
                LM_ERROR ((LOG_ERROR, "%p\n%a", "pipe", 1));
        // Enable message-oriented mode instead of
        // bytestream mode.
        int arg = RMSGN;

        if (ioctl (fds[0], I_SRDOPT, arg) == -1
                || ioctl (fds[1], I_SRDOPT, arg) == -1)
                return -1;
}





メインプログラムは適当な通信チャネルをオープンすることから開始される。続いてプログラムは子プロセスをフォークし、それぞれのプロセスでPing Pongイベントハンドラのオブジェクトをcallbackという名前で具体化し、Reactorのインスタンスに、I/Oベース、シグナルベース、タイマーベースのイベントにcallbackを登録し、イベントループに入る。

int main (int argc, char *argv[])
{
        int     fds[2];
        Reactor reactor;

        init_fds (fds);

        pid_t pid = fork ();
        
        Ping_Pong callback (argv[1]);

        //Register I/O-based event handler
        reactor.register_handler (
                fds[pid == 0],
                &callback,
                Event_Handler::READ_MASK
                | Event_Handler::WRITE_MASK);

        //Register signal-based event handler
        reactor.register_handler (SIGINT, &callback);

        //Register timer-based event handler
        reactor.schedule_timer (&callback, 0, 10);

        /* Main event loop (run in each process) */
        while (callback.done () == false)
                reactor,handle_events ();
        return 0;
} 


タイマーベースとシグナルベースのイベントのcallbackイベントハンドラはReactorの中の適切なテーブルに保存される。 同様にReactorは、register_handlerがI/Oベースのイベントハンドラを登録するために実行された時、適切なハンドラを内部テーブルに保存する。アプリケーションが続いてメインのイベントループReactor::handle_eventsを実行すると、このハンドルは内部でOSのI/O非多重化のシステムコール(select, poll等)への引数として渡される。

実行時に予めイベントハンドラcallbackオブジェクトにて登録されたものに対応した、入出力、シグナル、タイマーイベントが発生すると、Reactorは自動的にイベントをイベントハンドラオブジェクトの適当なメソッドにディスパッチする。callbackオブジェクトのディスパッチされたメソッドは、アプリケーションに応じた機能(例:通信チャネルへのメッセージの書き込み、メッセージの読み込み、プログラムの終了させるためのフラグをセットする。)を実行しなければならない。これらの部品の相互動作関係を図4のオブジェクト関係図(object interaction diagram)に示す。

2.2 IPC_SAP: ローカルとリモートのIPC機構

ACEはIPC_SAP(InterProcess Communication Service Access Point)基本クラスを基とした、多くのクラスカテゴリを提供している。IPC_SAPは、コネクション指向とコネクションなしのプロトコルの、標準的な入出力ベースのOSのローカルとリモートのIPC機構を隠蔽化している。図5のように、これらのクラスカテゴリには、SOCK SAP(socket APIを隠蔽化), TLI SAP(TLI API), SPIPE SAP(UNIX SunOS 5.x STREAMパイプAPI), FIFO SAP(UNIX named pipe API)を含む。

それぞれのクラスカテゴリは継承の階層になっている。全てのサブクラスは、ローカルあるいはリモート通信機構のサブセットに、よく定義されたインタフェースを提供している。同時に、階層中のサブクラスは個々の通信の枠組み(communication abstraction)(インターネットドメインあるいはUNIXドメイン)全体の機能を構成している。単独の関すを利用するのに比べてクラスを使用することは、次のようにネットワークプログラミングを容易にする。

以下の章ではIPC SAPの個々のクラスカテゴリについて議論する。

2.2.1 SOCK SAP

SOCK SAP C++クラスカテゴリはアプリケーションにインターネットドメインとUNIXドメインプロトコルファミリのオブジェクト指向インタフェースを提供する。アプリケーションは基礎にあるインターネットドメインとUNIXドメインsocketタイプの機能を、図6に示す適切なSOCK SAPのサブクラスを継承あるいは具体化しアクセスする。SOCK*サブクラスはインターネットドメインの機能を隠蔽化し、LSOCK*サブクラスはUNIXドメインの機能を隠蔽化する。図6に示すように、サブクラスはさらに、(1)*Dgramコンポーネント(信頼性のない、非接続型のメッセージ指向の機能を提供する)vs. Streamコンポーネント(信頼性のあるコネクション指向のバイトストリーム機能を提供する)と(2)*Acceptorコンポーネント(サーバがよく使う、コネクションの確立機能を提供する)vs. *Streamコンポーネント(クライアントとサーバの両方で使われる、双方向の倍とストリームデータ転送機能を提供する)に分解される。

C++ラッパーを使ってsocketインタフェースを隠蔽化することは次の3点のメリットがある。 (1)コンパイル時に、捕らえ難いアプリケーションの型違反を検出する。(2)アプリケーションの移植性を増す、プラットフォームに依存しない転送レベルインタフェースを提供しする。(3)低レベルのネットワークプログラミングの細かい点に費やしていた労力を大きく減少させる。後者のポイントを説明するために、次のプログラミング例で、LANのサブネット上で、指定されたポート番号を見ている全てのサーバにメッセージをブロードキャストするために、SOCK Dgram Bcastクラスを使用するクライアントアプリケーションを実装している。

int
main (int argc, char *argv[])
{
	SOCK_Dgram_Bcast bsap (sap_any);
	char *msg;
	unsigned short b_port;

	msg = argc > 1 ? argv[1] : "hello world\n";
	b_port = argc > 2 ? atoi (argv[2]) : 12345;

	if (b_sap.send (msg, strlen (msg),
			b_port) == -1)
		perror ("can't send broadcast"), exit (1);
	exit (0);
}

socketインタフェースを直接使って作成する多くの行数のコードとこれを比較してみるとよい。

2.2.2 TLI SAP

TLI SAP クラスカテゴリは、SYSTEM VのTransport Layer Interface(TLI)の C++のインタフェースを提供する。TLI SAPの継承階層は、ソケットのSOCK SAP C++ ラッパーとほぼ同じである。一番の違いはTLIとTLI SAPがUNIXドメインのプロトコル ファミリのインタフェースを定義しない点である。C++の特徴(デフォールトパラメータ やテンプレート)とtirdwr(STREAMSモジュールのリード/ライト コンパチビリティ) が一緒になることで、ソケットベースあるいはTLIベースの転送インタフェースの どちらでも正しく扱えるように、コンパイル時にパラメタライズされた アプリケーションを開発することが簡単になっている。

次のコードは、アプリケーションがどのようにC++テンプレートを使って IPC機構をパラメタライズするかを示している。このコードは3.1章で説明する 分散ロギング機能から抜出したものである。以下のコードではEvent Handlerから 派生したサブクラスが転送I/Fとプロトコルアドレスクラスでパラメータ化されて いる。

/* Logging_IO header file */
template 
class Logging_IO : public Event_Handler
{
public:
	Logging_IO (void);
	virtual ~Logging_IO (void);

	virtual int handle_close (HANDLE);
	virtual int handle_input (HANDLE);
	virtual HANDLE get_handle (void) const
	{
		return this->peer_stream_.get_handle ();
	}

protected:
	PEER_STREAM peer_stream_;
};

OSの特性(BSDベースのSunOS 4.xかSYSTEM VベースのSunOS 5.x)によって、 ロギングアプリケーションはClient Handlerクラスを以下に示すようにSOCK SAP あるいはTLI SAPを用いて具体化する。

/* Logging application */
class Logging_IO
#if defined (MT_SAFE_SOCKETS)
: public Logging_IO
#else
: public Logging_IO
#endif /* MT_SAFE_SOCKETS */
{
	/* ... */
};

このテンプレートによるフレキシビリティの増加は、複数のOSの上で動作可能な アプリケーションを開発する上で、非常に便利である。特に、転送インタフェース によりアプリケーションをパラメタライズすることが可能なことは、スレッドセーフ ではないSun0S 5.2のsocketの実装やSunOS 4.xの不具合の多いTLIの実装といった、 様々なSunOSにまたがるものを作る場合必要である。

TLI SAPはまた、TLIインタフェースの多くの詳細からアプリケーションを 隔離する。例えば、qlen > 1の並列サーバの、不明確で間違いやすい動作をする t_listenとt_acceptを使わなければならない微妙なアプリケーションレベルのコード は、TLI Acceptorクラスのacceptメソッドに隠蔽化される。このメソッドは、 クライアントからの接続要求を受付ける。C++のデフォルト引数によって、 acceptメソッドを呼出す標準の方法はTLI_SAPベースでもSOCK SAPベースでも 構文的には同じである。

2.2.2 SPIPE SAP

SPIPE SAPクラスカテゴリは、マウントされたSTREAMパイプをconnldのC++ ラッパーインタフェースを提供する。SunOS 5.xは、パイプハンドルを UNIXファイルシステムの指定された場所にマウントするfattachシステムコールを 提供する。サーバのアプリケーションは、パイプのマウントされた終端に、 connld STREAMモジュールを入れることで作られる。サーバと同じホストマシンで 動いているクライアントアプリケーションが正しい順序でマウントされたパイプ に結びつけられたファイル名をオープンすると、クライアントとサーバは 非多重・双方向の通信チャネルのハンドルをそれぞれ得る。

SPIPE SAPの継承階層は、SOCK SAPやTLI SAPのものと同じである。 それはSOCK SAP LSOCK* クラス(それ自身がUNIXドメインsocketsを隠蔽する)と 類似の機能を提供する。しかしながら、SPIPE SAPはSTREAMモジュールを SPIPE SAPの終端でPush,Pop可能にするため、よりフレキシブルと言える。 SPIPE SAPはまた、同一ホスト内でのプロセス間、スレッド間のバイトストリーム と優先度付メッセージ指向のデータの双方向の配送をサポートする。

2.2.4 FIFO SAP

FIFO SAPクラスカテゴリは、UNIX名前付パイプ機構(FIFOとも呼ばれる)を隠蔽化 する。 STREAMパイプと異なり名前付パイプは、1つあるいは複数の送信元から1つの受信者 への単方向通信だけを提供している。さらに異なる送信者からのメッセージもすべて 同じ通信チャネルに入る。このため、受信側がメッセージの送信元を特定するための、 ある種の非多重化識別子をそれぞれのメッセージに明示的に入れる必要がある。 SunOS 5.xのSTREAMSベースの名前付パイプの実装は、メッセージ指向とバイトストリーム指向のデータ配送の両方の仕組を提供する。対照的にSunOS 4.xはバイトストリーム 指向の名前付パイプだけを提供する。従って、固定長メッセージが常に使われる場合 を除き、SunOS 4.xで名前付パイプで送られる個々のメッセージは、受信側が 通信チャネルのバイトストリームからメッセージを取出せるように、何らかの バイト数か、特別な終了シンボルによって区別する必要がある。この制限を軽減する ために、SunOS 4.x FIFO SAPの実装はSunOS 5.xで可能なメッセージ指向の仕組を エミュレートするロジックを含んでいる。

2.2.3 その他通信機構

sockets,TLIといった、ハンドルベースI/O通信機構の隠蔽化に加え、ACEは SYSTEM V UNIX IPCメカニズムとメモリマップドファイルのC++ラッパーを提供する。

2.3 並列性:マルチスレッドと同期機構

ACEはSolarisとPOSIX Pthreadsのマルチスレッドの同期の機構を隠蔽化するC++ ラッパー(Mutex, Condition, Semaphore, RW Lock)を含む。これらのラッパーは C++のクラス中に現れ、スレッドと同期機構の典型的な使われ方を単純化する、 同期オブジェクトの初期化を自動化する。例えば、次のコードは、SunOSの、 典型的な共有リソース管理クラスのためのmutex_tとcond_t同期機構を実現する C++ラッパーである。

class Resource_Manager
{
public:
	Resource_Manager (u_int initial_resources)
		: resource_add_ (this->lock),
		resource_ (initial_resources) {}
	int acquire_resources (u_int, amount_wanted)
	{
		this->lock_.acquire ();

		while (this->resources_ < amount_wanted) {
			this->waiting_++;
			/* Block until resources are released */
			this->resource_add_.wait ();
		}
		this->resource_ -= amount_wanted;
		this->lock_.release ();
	}

	int release_resources (u_int amount_released)
	{
		this->lock_.acquire ();
		this->resources_ += amount_released;
		if (this->waiting_ == 1) {
			this->waiting_ = 0;
			this->resource_add_.signal ();
		}
		else if (this->waiting_ > 1) {
			this_waiting_ = 0;
			this->resource_add_.broadcast ();
		}
		this->lock_.release ();
	}
	// ...
private:
	Mutex lock_;
	Condition resource_add_;
	u_int resources_;
	u_int waiting_;
	// ...
};

どのようにConditionのオブジェクトresource_add_のコンストラクタが、 がMutexのオブジェクトlockとCondition_オブジェクトを結びつけるかに注意が 必要である。これは、SunOSのcond_tmcond_waitに比べ、Condition::waitコールの インタフェースを単純化している。

さらに、Mutexラッパーはreleaseを呼び忘れる(プログラマの怠慢やC++の 例外処理による)という潜在的に間違いやすい、複数のスレッドの制御の同期に、 よりエレガントな方法を提供している。より強いアプリケーションのため、ACEの 同期機能はC++のクラスコンストラクタとデストラクタの仕組を強化している。 Mutexロックが自動的に要求され・解放されることを確かにするために、 ACEはGuardというヘルパークラスを提供する。以下にその定義を示す。

template 
class Guard
{
public:
	Guard (Mutex &m): lock (m) {
		this->lock_.acquire ();
	}
	~Guard (void) {
		this->lock_.release ();
	}
private:
	MUTEX &lock_;
}

GuardのオブジェクトはMutexを要求するコードの固まりを定義し、その固まりから 抜出る時には自動的に解放する。

Guardクラスは相互排他機能によりパラメタライズされたテンプレートとして定義 されていることに注意が必要。いくつかの異なるタイプのmutexの方法がある。 それぞれの相互排他機能は同じインタフェース(acquire/release)を共有するが、 異なるシリアライゼーションと性能の特質を持つ。ACEは、非リカーシブとリカーシブ の2つのタイプの相互排他をサポートしている。