Re: Fwd: Re: Thread local storage: add example

Heinrich Schuchardt <[email protected]>
Newsgroups gmane.comp.gnu.glpk
Message-ID <[email protected]>
Hello Andrew,

in the appended multiseed.c I have added a test to check if the GLPK
library supports thread local memory.

Further I added more comments and completed the error handling.

Best regards

Heinrich Schuchardt

On 01/14/2017 03:49 PM, Andrew Makhorin wrote:
> Hi Heinrich,
> 
>> it seems you missed the mail below.
>>
>> Could you, please, add the appended README.md to tls/examples.
> 
> Done.
> 
>> I understand that you do not want to create env before checking if TLS
>> is configured. But, please, do not call abort() in glp_config().
>>
>> Otherwise your abort statement will stop the complete application.
>> Imagine that being the webserver for a site on which a webapp is
>> deployed which calls the GLPK library.
>>
>> Either simply return NULL. Or define a value with signifies an error:
>>
>> glpk.h:
>> #define GLP_INVALID ((const void *) -1)
>>
>> env/env.c:
>> const char *glp_config(const char *option)
>> {
>> ...
>>    return GLP_INVALID;
>> ...
>> }
>>
> 
>> I guess the best solution will be to return the empty string "" when
>> an
>> option is not set and NULL if the option does not exist.
>>
>> Then we can use
>>
>> const char *ret = get_config("TLS");
>> if (!ret || !*ret) {
>>         fprintf(stderr, "TLS not supported");
>>         pthread_exit(NULL);
>> }
> 
> I changed glp_config to return NULL for any unknown option as if this
> option were disabled.
> 
>> if a multithreaded application wants to determine if GLPK supports a
>> feature we now supply glp_config which does not initialize env.
>> I guess we should do the same for glp_version.
>>
>> This makes env smaller and can safely be called even if the library is
>> not TLS enabled.
>>
>> #define str(s) # s
>> #define xstr(s) str(s)
>>
>> const char *glp_version(void)
>> {     return xstr(GLP_MAJOR_VERSION) "." xstr(GLP_MINOR_VERSION);
>> }
> 
> Done.
> 
> Please see an updated version of glpk here:
> http://sourceforge.net/projects/noumenon/files/tmp/
> (Note that this is *not* an official release.)
> 
> 
> Best regards,
> 
> Andrew Makhorin
> 
>

_______________________________________________
Help-glpk mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/help-glpk
multiseed.c (text/x-csrc, 6.3 KB)
/* multiseed.c (multithreading demo) */

/***********************************************************************
*  This code is part of GLPK (GNU Linear Programming Kit).
*
*  Author: Heinrich Schuchardt <[email protected]>
*
*  Copyright (C) 2017 Andrew Makhorin, Department for Applied
*  Informatics, Moscow Aviation Institute, Moscow, Russia. All rights
*  reserved. E-mail: <[email protected]>.
*
*  GLPK is free software: you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  GLPK is distributed in the hope that it will be useful, but WITHOUT
*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
*  License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/

/*
 * This program demonstrates running the GLPK library with multiple threads.
 *
 * When called the program requires two arguments:
 *
 * filename - the name of the MathProg model to be solved
 * threads  - the count of parallel threads to be run.
 *
 * Each thread is run with a different seed for the random number generator
 * provided by the GLPK library.
 */

#include <glpk.h>
#include <malloc.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "thread.h"

#define BUFLEN 256

/* Task descriptor */
struct task {
   pthread_t tid;
   char *filename;
   int seed;
   size_t pos;
   char buf[BUFLEN + 1];
   int line;
   jmp_buf jmp;
};

/* Mutex for console output */
pthread_mutex_t mutex;

/* Console output handler */
int term_hook(void *info, const char *text)
{
   struct task *task = (struct task *) info;
   size_t len = strlen(text);

   /* Lock mutex so this is the only task creating console output. */
   pthread_mutex_lock(&mutex);

   /* Append the new text to the buffer. */
   if (task->pos + len > BUFLEN) {
      printf("%02d-%05d %s%s", task->seed, ++task->line, task->buf, text);
      task->pos = 0;
      task->buf[0] = 0;
   } else {
      strcpy(task->buf + task->pos, text);
      task->pos += len;
   }

   /* If a complete line is available, send it to the console. */
   if (strchr(task->buf, '\n')) {
      printf("%02d-%05d %s", task->seed, ++task->line, task->buf);
      task->pos = 0;
      task->buf[0] = 0;
   }

   /* Unlock the mutex. */
   pthread_mutex_unlock(&mutex);

   /* Disable default output. */
   return -1;
}

/* Error handler */
void error_hook(void *info)
{
   struct task *task = (struct task *) info;

   term_hook(task, "Error caught\n");
   glp_free_env();
   longjmp(task->jmp, 1);
}

void worker(void *arg)
{
   struct task *task = (struct task *) arg;
   int ret;
   glp_prob *lp;
   glp_tran *tran;
   glp_iocp iocp;

   if (setjmp(task->jmp)) {
      /* If an error is caught leave the function. */
      return;
   }

   /* Set the error handler. */
   glp_error_hook(error_hook, task);

   /* Set the console output handler. */
   glp_term_hook(term_hook, arg);

   glp_printf("Seed %02d\n", task->seed);

   /* Create the problem object. */
   lp = glp_create_prob();
   if (!lp) {
      glp_error("Out of memory\n");
   }

   /* Create the MathProg translator workspace. */
   tran = glp_mpl_alloc_wksp();
   if (!lp) {
      glp_error("Out of memory\n");
   }

   /* Set the pseudo random number generator seed. */
   glp_mpl_init_rand(tran, task->seed);

   /* Read the model file. */
   ret = glp_mpl_read_model (tran, task->filename, GLP_OFF);
   if (ret != 0) {
      glp_error("Model %s is not valid\n", task->filename);
   }

   /* Generate the model. */
   ret = glp_mpl_generate(tran, NULL);
   if (ret != 0) {
      glp_error("Cannot generate model %s\n", task->filename);
   }

   /* Build the problem. */
   glp_mpl_build_prob(tran, lp);

   /* Solve the problem. */
   glp_init_iocp(&iocp);
   iocp.presolve = GLP_ON;
   ret = glp_intopt(lp, &iocp);
   if (ret == 0) {
      /* Execute the post solve part of the model. */
      glp_mpl_postsolve(tran, lp, GLP_MIP);
   }

   /* Release the memory. */
   glp_mpl_free_wksp (tran);
   glp_delete_prob(lp);

   if (0 == task->seed % 3) {
      glp_error("Voluntarily throwing an error in %s at line %d\n",
                __FILE__, __LINE__);
   }

   glp_term_hook(NULL, NULL);

   glp_error_hook(NULL, NULL);

   glp_free_env();
}

#ifdef __WOE__
DWORD run(void *arg)
{
#else
void *run(void *arg)
{
#endif
   worker(arg);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
   int i, n, rc;
   struct task *tasks;

   /* Make sure thread local memory is used by the GLPK library. */
   if (!glp_config("TLS")) {
      printf("The loaded GLPK library does not support thread local memory.\n"
        "You need a version of the library configured with "
	"--enable-reentrant=yes to run this program.\n");
      exit(EXIT_FAILURE);
   }

   /* Check program arguments. */
   if (argc != 3) {
      printf("Usage %s filename threads\n"
             "  filename - MathProg model file\n"
             "  threads  - number of threads\n",
             argv[0]);
      exit(EXIT_FAILURE);
   }

   /* Parse the arguments. */
   n = atoi(argv[2]);
   if (n > 50) {
      printf("Number of threads is to high (> 50).\n");
      exit(EXIT_FAILURE);
   }
   if (n <= 1) {
      printf("Need positive number of threads\n");
      exit(EXIT_FAILURE);
   }

   /* Allocate memory for the task descriptors. */
   tasks = calloc(n, sizeof(struct task));
   if (!tasks) {
      printf("Out of memory");
      exit(EXIT_FAILURE);
   }

   /* Create a mutex for console output. */
   pthread_mutex_init(&mutex, NULL);

   /* Create the threads. */
   for (i = 0; i < n; ++i) {
      tasks[i].filename = argv[1];
      tasks[i].seed = i + 1;
      tasks[i].pos = 0;
      tasks[i].buf[0] = 0;
      tasks[i].line = 0;
      rc = pthread_create(&tasks[i].tid, NULL, run, &tasks[i]);
      if (rc) {
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(EXIT_FAILURE);
      }
   }

   /* Wait for all threads to complete. */
   for (i = 0; i < n; ++i) {
      pthread_join(tasks[i].tid, NULL);
   }

   /* Destroy the mutex. */
   pthread_mutex_destroy(&mutex);

   return EXIT_SUCCESS;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.