RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

thread.c

/* $Id: thread.c,v 1.35 2005/02/04 04:59:59 jmckee Exp $ */
/*
 * Copyright (C) 1998-2003 RSA Security Inc.
 *
 * This file shall only be used to demonstrate how to interface to an
 * RSA Security Inc. licensed development product.
 *
 * You have a royalty-free right to use, reproduce and distribute this
 * demonstration file, provided that you agree that RSA Security Inc.
 * has no warranty, implied or otherwise, or liability for this
 * demonstration file (including any modified version).  This software
 * is provided "as is" without warranties or representations of any
 * kind. RSA Security disclaims all conditions and warranties, statutory
 * and otherwise, both express and implied, with respect to the software,
 * its quality and performance, including but not limited to, all
 * implied warranties of merchantability, fitness for a particular
 * purpose, title and noninfringement of third party rights. Without
 * limiting the foregoing, RSA Security does not warrant that the
 * software is error-free or that errors in the product will be
 * corrected. You agree that RSA Security shall not be liable for any
 * direct, indirect, incidental, special, consequential, punitive or
 * other damages whatsoever resulting from your use of this software
 * or any modified version.
 *
 *
 */

#include "r_prod.h"

#define MAX_THREAD_NUMBER       256

/* Include the header files for the callbacks */
#include "../cb/thread/mt_cb.h"


void app_thread_main(void *arg);

BIO *bio_err = NULL;
BIO *bio_out = NULL;

static int app_lockid = 0;
static int start_lockid = 0;
static int shared_var = 0;
static int no_concurrency = 0;

int no_lock = 0;                /* Switch on for no locking - should break */

/*
 * 10 threads and 1000 loops should produce a non-zero result
 * in the test if locking is disabled via -no_lock
 */
int thread_number = 3;
int number_of_loops = 3;

int main(int argc, char *argv[])
{
    R_LIB_CTX *lib_ctx = NULL;
    int ret = R_ERROR_FAILED;
    int badop = 0;
    int trace = 0;
    int stats = 0;
#ifdef NO_SOFTWARE_CRYPTO
    R_FIPS140_OPERATING_MODE_T  operating_mode = PRODUCT_FIPS140_MODE_DEFAULT;
#endif /* NO_SOFTWARE_CRYPTO */

    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
    bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
    BIO_set_flags(bio_err, BIO_FLAGS_FLUSH_ON_WRITE);
    BIO_set_flags(bio_out, BIO_FLAGS_FLUSH_ON_WRITE);

    argc--;
    argv++;

    while (argc >= 1)
    {
        if (Strcmp(*argv, "-trace") == 0)
        {
            trace = 1;
        }
        else if (Strcmp(*argv, "-no_lock") == 0)
        {
            no_lock = 1;
        }
        else if (Strcmp(*argv, "-stats") == 0)
        {
            stats = 1;
        }
#ifdef NO_SOFTWARE_CRYPTO
        else if (Strcmp(*argv, "-no_fips140") == 0)
        {
            operating_mode = NON_FIPS140_MODE;
        }
        else if (Strcmp(*argv, "-fips140_ssl") == 0)
        {
            operating_mode = FIPS140_SSL_MODE;
        }
#endif /* NO_SOFTWARE_CRYPTO */

        else if (Strcmp(*argv, "-threads") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            thread_number = atoi(*(++argv));
            if (thread_number == 0)
            {
                thread_number = 1;
            }
            if (thread_number > MAX_THREAD_NUMBER)
            {
                thread_number = MAX_THREAD_NUMBER;
            }
        }
        else if (Strcmp(*argv, "-loops") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            number_of_loops = atoi(*(++argv));
            if (number_of_loops == 0)
            {
                number_of_loops = 1;
            }
        }
        else
        {
            BIO_printf(bio_err, "Unknown option %s\n", *argv);
            badop = 1;
            break;
        }
        argc--;
        argv++;
    }

    if (badop)
    {
bad:
        BIO_printf(bio_err,
            "Usage: thread [-trace] [-no_lock] [-stats] [-loops N] [-threads N]\n");
#ifdef NO_SOFTWARE_CRYPTO
        BIO_printf(bio_err,
            "       [-no_fips140] [-fips140_ssl]\n");
#endif /* NO_SOFTWARE_CRYPTO */
        goto end;
    }

    /*
     * Create the library context. Retrieve the default resource list and
     * create a library context to provide access to all configurable aspects
     * of the library.
     */
 #ifdef NO_SOFTWARE_CRYPTO
     /*
      * For FIPS140 shared library builds set the operating mode required
      * first
      */
     switch (operating_mode)
     {

#ifdef PRODUCT_FIPS140_ENABLE_FIPS140_OPERATING_MODE
     case FIPS140_MODE:
         PRODUCT_FIPS140_ENABLE_FIPS140_OPERATING_MODE();
         break;
#endif

     case NON_FIPS140_MODE:
         PRODUCT_FIPS140_ENABLE_NON_FIPS140_OPERATING_MODE();
         break;

     case FIPS140_SSL_MODE:
         PRODUCT_FIPS140_ENABLE_FIPS140_SSL_OPERATING_MODE();
         break;
     }
 #endif /* NO_SOFTWARE_CRYPTO */

    /* Perform the standard library initialization */
    if (PRODUCT_LIBRARY_NEW(PRODUCT_DEFAULT_RESOURCE_LIST(),
        R_RES_FLAG_DEF, &lib_ctx) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "ERROR: unable to create library context\n");
        goto end;
    }

    /*
     * Activate thread tracing. Trace the activities of the threads to view
     * when threads start and stop.
     */

    /*
     * The APP* routines are in the relevant OS-specific included files
     * located in ../cb/mt_{thread_api_name}.c
     */
    APP_thread_trace(trace);

    /* Reserve a lock identifier from the library for local use */

    /* Register a new lock with the library for local use */
    app_lockid = R_lockid_new("applock");
    start_lockid = R_lockid_new("startlock");
    if ((app_lockid <= 0) || (start_lockid <= 0))
    {
        BIO_printf(bio_err, "Unable to retrieve a lockid\n");
        goto end;
    }
    BIO_printf(bio_out, "app_lockid   = %ld\n", app_lockid);
    BIO_printf(bio_out, "start_lockid = %ld\n", start_lockid);

    /*
     * Register the locking callbacks. This involves initializing all the
     * locks. R_lock_num() returns the number of locks required, including the
     * locally added lock.
     */

    /* Register the callbacks to use to perform locking */
    APP_locking_cb_add();


    /*
     * Execute the threads. Ensure the threads have not been initialized and
     * then create all the new threads. Wait for the last thread to finish and
     * then show the thread statistics to view the thread usage.
     */

    /* Start the threads */
    if (APP_thread_initialize(thread_number))
    {
        int i;

        R_lock_w(start_lockid);

        for (i = 0; i < thread_number; i++)
        {
            APP_thread_create((void (*)(void *)) app_thread_main,
                              (void *) NULL, NULL);
        }
        R_unlock_w(start_lockid);

        /* Wait for all the threads to finish */
        APP_thread_finalize(thread_number);

    }

    if (stats)
    {
        APP_thread_show_stats();
    }

    /*
     * Check the state of the shared resources. The resources should be zero to
     * indicate that the locking in each thread worked correctly.
     */
    if (no_concurrency)
        goto end;

    BIO_printf(bio_out, "shared_var: %d - %s\n", shared_var,
               shared_var == (number_of_loops*thread_number) ? "OK" : "ERROR");

    if (shared_var == (number_of_loops*thread_number))
    {
        ret = R_ERROR_NONE;
    }

end:
    /*
     * Clean up. Destroy the dynamically allocated objects and return an exit
     * code.
     */

    APP_locking_cb_delete();

    if (bio_err != NULL)
    {
        BIO_free(bio_err);
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    return(R_ERROR_EXIT_CODE(ret));
}

void app_thread_main(void *arg)
{
    int i;
    int value;

    /*
     * If the threading code is working, all threads will be kicked off and hit
     * this point very quickly. Since each thread then sleeps for at least 1
     * second, all of them should see a shared_var value of 0 at this point.
     * If they do not, it is because the threads are not running concurrently.
     */
    if (shared_var != 0)
    {
        BIO_printf(bio_out, "Thread is not concurrent\n");
        no_concurrency=1;
        return;
    }

    /*
     * Try to get a read lock, so everyone starts at the same time.
     * This will deadlock if the threads are not concurrent.
     */
    R_lock_r(start_lockid);
    R_unlock_r(start_lockid);

    BIO_printf(bio_out, "thread start %ld\n", R_thread_id());

    for (i = 0; i < number_of_loops; i++)
    {
       /*
        * Retrieve the local lock and use it to indicate that data is about to
        * be updated
        */

        if (!no_lock)
        {
            R_lock_w(app_lockid);
        }

       /* Perform the update on the shared resource */

        value=shared_var+1;

       /*
        * Make a system call to increase the chances of the thread being
        * swapped out for another. Note this is the system call that must be
        * protected.
        */

        /*
         * Sleep here to increase the chance of another thread being executed
         */

        R_sleep(1);

       /* Undo the update on the shared resource */

        shared_var=value;

       /*
        * Release the local lock to indicate that the data has finished being
        * updated
        */

        if (!no_lock)
        {
            R_unlock_w(app_lockid);
        }

    }

    /* Signal the end of the current thread */
    BIO_printf(bio_out, "thread exit %ld\n", R_thread_id());
}


Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1