Singleton/Lazy Initialization

A common programming pattern is the singleton object, which is often initialized lazily. The idea is that there is some unique object that we don't want to initialize until (unless) it is needed. Here is an example:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
  
  
  
  
  
  
  
  
  
  
  
special_object_t *get_special_object( void )
{
    
static special_object_t o;
    
static int initialized = 0;
    
if( !initialized )
    
{
        
init_special_object( &o );
        
initialized = 1;
    
}
    
return &o;
}

This code works fine in a sequential context, but fails badly in a multithreaded context. In the (very) special case that the call to init_special_object is guaranteed to not yield, this code is actually activity-safe, because there is nothing else to trigger a yield in this code, so the whole procedure will execute atomically. If there is some chance that init_special_object will yield, we need something more to prevent the situation where multiple activities enter the conditional block and initialize the special object more than once.

As long as init_special_object is guaranteed to run in a modest amount of time, you can implement lazy initialization in Charcoal like so:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
  
  
  
  
  
  
  
  
  
  
  
  
  
  
special_object_t *get_special_object( void )
{
    
static special_object_t o;
    
static int initialized = 0;
    
unyielding
    
{
        
if( !initialized )
        
{
            
init_special_object( &o );
            
initialized = 1;
        
}
    
}
    
return &o;
}

If init_special_object might run for a long time, or block indefinitely, this is not a good solution. Our activities will be starved. We want to block only activites that need the special object, instead of all activities. We can use the standard implementation that works for multithreaded code, too.

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
special_object_t *get_special_object( void )
{
    
static special_object_t o;
    
static mutex_t mtx = STATIC_MUTEX_INIT;
    
static int initialized = 0;
    
synchronized( &mtx )
    
{
        
if( !initialized )
        
{
            
init_special_object( &o );
            
initialized = 1;
        
}
    
}
    
return &o;
}

Why is this an interesting example if we end up with more or less exactly the same code you'd write in a multithreaded context anyway? The reason is that synchronization operations between activites are extremely cheap. The whole reason that the double-checked locking issue has been discussed and written about so much is that some programmers think it's important to avoid synchronization operations whenever possible. It's debatable how much of a performance issue this really is in languages like Java. But in Charcoal there is not even a debate. Synchronization operations between activities so cheap that there is no reason to avoid them for performance considerations.

If you happen to be clinically insane and won't tolerate even a small handful of instructions for acquiring and releasing the mutex, you can actually implement some of the dodgier versions of double-checked locking in Charcoal.

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
special_object_t *get_special_object( void )
{
    
static special_object_t o;
    
static mutex_t mtx = STATIC_MUTEX_INIT;
    
static int initialized = 0;
    
if( initialized )
        
return &o;
    
synchronized( &mtx )
    
{
        
if( !initialized )
        
{
            
init_special_object( &o );
            
initialized = 1;
        
}
    
}
    
return &o;
}

This version works, but is more complex than the previous one, and I cannot imagine that the performance difference is perceptible in any real application.


Creative Commons License
Charcoal Programming Language by Benjamin Ylvisaker is licensed under a Creative Commons Attribution 4.0 International License