db4oでの排他制御その2

チュートリアルによるとセマフォを使って自前で行うみたい。ただ、セマフォ自体は名前に対する排他制御なので、ロックしたいオブジェクトの識別子を使って擬似的にオブジェクトロックを行うことになるのかな。
昨日のclient.exeに排他制御を入れてみました。

using System;
using System.Collections.Generic;
using com.db4o;
using com.db4o.ext;
using com.db4o.messaging;

public class App {
    const string host = "localhost";
    const int port = 1234;
    const string user = "user";
    const string pass = "pass";
    const int wait = 1000;  // ロック待ち時間
    public static void Main(string[] args) {
        ObjectContainer client = null;
        try {
            client = Db4o.OpenClient (host, port, user, pass);

            // 更新モードでクエリー
            IList<Pilot> pilots = Query (client, true);
            foreach (Pilot pilot in pilots) {
                pilot.AddPoints (1);
                client.Set (pilot);
            }
            Console.WriteLine ("Before Commit !");
            Console.ReadLine ();

            client.Commit ();
            // ロックの解放
            Unlock (client, pilots);

            Console.WriteLine ("Commit Done!");
            Console.ReadLine ();

            pilots = Query (client, false);
            foreach (Pilot pilot in pilots)
                Console.WriteLine (pilot);
        }

        finally {
            if (client != null)
                client.Close ();
            Console.WriteLine ("Closed.");
        }
        Console.WriteLine ("Exit.");
    }

    static IList<Pilot> Query(ObjectContainer client, bool for_update) {
        IList<Pilot> pilots = client.Query<Pilot>(
                delegate (Pilot pilot) 
                { 
                    return true;
                });
        if (for_update) {
            List<Pilot> locked = new List<Pilot> ();
            foreach (Pilot pilot in pilots) {
                long id = client.Ext ().GetID (pilot);
                // waitで指定した時間内にロックが取得できない場合は、ロック失敗とする
                if (client.Ext ().SetSemaphore (id.ToString(), wait)) {
                    // 他のクライアントが値を変更している可能性があるのでリフレッシュ
                    client.Ext ().Refresh (pilot, int.MaxValue);
                    locked.Add (pilot);
                }
                else {
                    Console.WriteLine ("Timeout!");
                    break;
                }
            }
            if (pilots.Count != locked.Count) {
                // pilotsに含まれるオブジェクトを全て同時にロック出来ない場合は、
                // このクエリーは無効にする
                Unlock (client, locked);
                locked.Clear ();
            }
            pilots = locked;
        }
        return pilots;
    }

    static void Unlock (ObjectContainer client, IList<Pilot> pilots) {
        foreach (Pilot pilot in pilots) {
            long id = client.Ext ().GetID (pilot);
            client.Ext ().ReleaseSemaphore (id.ToString ());
        }
    }
}

どの単位でロックをかけるべきか、ロックが失敗した場合はどうするか、など、まだまだ分からないことばかりなので、私はオブジェクトデータベースを自信を持って使えないですね。(^^;