Passing a function to another function?

Recently I wrote an article about how I handled semaphores in a Drupal project. I want to talk about how I actually implemented it. I needed more than one function to use the semaphore. The goal was to prevent one function to be called when the semaphore was active. The semaphore would get activated when the function would run. Basically, prevent the functions from running at the same time.

These functions were doing some hard work that could take 2-30 minutes. Having 6 (or more) of them running at the same time was slowing down the server. So the semaphore is to prevent the server from crashing but allow these functions to be executed.

I created a function like this:

/**
 * Wrapper that prevent a function from being called in paralell.
 * This is meant for functions that takes a lot of time to run and
 * cannot be run when another process already runs it.
 * This is because if the project takes a lot of time to run
 * and cron tried to start a new one, we do not want the
 * server to crash.  This prevent the second call to run if
 * the first one is not finished.
 *
 * Here is an example on how to use it.
 * project_wrap_semaphore('print', array('Hello World'));
 *
 * @param $callback
 *   Function name to be called only once at a time.
 * @param $args
 *   Arguments for the callback function.
 */

function project_wrap_semaphore($callback, $args = array()) {
  $semaphore = variable_get('project_semaphore', FALSE);

  if ($semaphore) {
    if ((time() - $semaphore) <= (3600*3)) {
      // project is still running normally.
      watchdog('project', t('Attempting to re-run project while it is already running.'), WATCHDOG_WARNING);
      return;

    }
    // Either project has been running for more than 3 hours or the semaphore
    // was not reset due to a database error.
    watchdog('project', t('project has been running for more than 3 hours and is most likely stuck.'), WATCHDOG_ERROR);

    // Release project semaphore
    variable_del('project_semaphore');
  }

  // Register shutdown callback
  register_shutdown_function('drupal_project_cleanup');

  // Lock project semaphore
  variable_set('project_semaphore', time());

  call_user_func_array($callback, $args);

  // Record project time
  variable_set('project_last', time());
  watchdog('project', t('project run completed.'), WATCHDOG_NOTICE);

  // Release project semaphore
  variable_del('project_semaphore');

  // Return TRUE so other functions can check if it did run successfully
  return TRUE;

}

/**
 * Shutdown function for project cleanup.
 */

function drupal_project_cleanup() {
  // See if the semaphore is still locked.
  if (variable_get('project_semaphore', FALSE)) {
    watchdog('project', t('project run exceeded the time limit and was aborted.'), WATCHDOG_WARNING);

    // Release project semaphore
    variable_del('project_semaphore');
  }
}

You will notice that the first thing you might find odd about this function is that it receives a callback as a parameters. You need to pass the name of a function to execute. This callback/function will get called only if the semaphore does not exists. This means you do not know in advance which function will be called since that is dynamic.

This is not a new concept. PHP already does that in a number of functions. Here's some PHP functions just to name a few:
- array_map()
- ob_start()
- preg_replace_callback()
- array_filter()
- usort()

There is much more.

I think that is totally natural to have functions that accepts functions as parameters.

What do you think?

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • You may post PHP code. You should include <?php ?> tags.
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>. Beside the tag style "<foo>" it is also possible to use "[foo]". PHP source code can also be enclosed in <?php ... ?> or <% ... %>.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options