Protecting The Objects Themselves
Prev | Chapter 6. Common Examples | Next

## Protecting The Objects Themselves

In these examples, we assumed that the objects (except the reference counts) never changed once they are created. If we wanted to allow the name to change, there are three possibilities:

- You can make cache_lock non-static, and tell people to grab that lock before changing the name in any object.

- You can provide a `cache_obj_rename` which grabs this lock and changes the name for the caller, and tell everyone to use that function.

- You can make the cache_lock protect only the cache itself, and use another lock to protect the name.

Theoretically, you can make the locks as fine-grained as one lock for every field, for every object. In practice, the most common variants are:

- One lock which protects the infrastructure (the cache list in this example) and all the objects. This is what we have done so far.

- One lock which protects the infrastructure (including the list pointers inside the objects), and one lock inside the object which protects the rest of that object.

- Multiple locks to protect the infrastructure (eg. one lock per hash chain), possibly with a separate per-object lock.

Here is the "lock-per-object" implementation:

    --- cache.c.refcnt-atomic	2003-12-11 15:50:54.000000000 +1100
    +++ cache.c.perobjectlock	2003-12-11 17:15:03.000000000 +1100
    @@ -6,11 +6,17 @@

     struct object
     {
    +        /* These two protected by cache_lock. */
             struct list_head list;
    +        int popularity;
    +
             atomic_t refcnt;
    +
    +        /* Doesn't change once created. */
             int id;
    +
    +        spinlock_t lock; /* Protects the name */
             char name[32];
    -        int popularity;
     };

     static spinlock_t cache_lock = SPIN_LOCK_UNLOCKED;
    @@ -77,6 +84,7 @@
             obj->id = id;
             obj->popularity = 0;
             atomic_set(&obj->refcnt, 1); /* The cache holds a reference */
    +        spin_lock_init(&obj->lock);

             spin_lock_irqsave(&cache_lock, flags);
             __cache_add(obj);

Note that I decide that the `popularity` count should be protected by the cache_lock rather than the per-object lock: this is because it (like the struct list_head inside the object) is logically part of the infrastructure. This way, I don't need to grab the lock of every object in `__cache_add` when seeking the least popular.

I also decided that the `id` member is unchangeable, so I don't need to grab each object lock in `__cache_find()` to examine the `id`: the object lock is only used by a caller who wants to read or write the `name` field.

Note also that I added a comment describing what data was protected by which locks. This is extremely important, as it describes the runtime behavior of the code, and can be hard to gain from just reading. And as Alan Cox says, “Lock data, not code”.

Prev | Up | Next
Exposing Objects Outside This File | Home | Chapter 7. Common Problems