1    	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2    	/*                                                                           */
3    	/*                  This file is part of the program and library             */
4    	/*         SCIP --- Solving Constraint Integer Programs                      */
5    	/*                                                                           */
6    	/*  Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB)                      */
7    	/*                                                                           */
8    	/*  Licensed under the Apache License, Version 2.0 (the "License");          */
9    	/*  you may not use this file except in compliance with the License.         */
10   	/*  You may obtain a copy of the License at                                  */
11   	/*                                                                           */
12   	/*      http://www.apache.org/licenses/LICENSE-2.0                           */
13   	/*                                                                           */
14   	/*  Unless required by applicable law or agreed to in writing, software      */
15   	/*  distributed under the License is distributed on an "AS IS" BASIS,        */
16   	/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17   	/*  See the License for the specific language governing permissions and      */
18   	/*  limitations under the License.                                           */
19   	/*                                                                           */
20   	/*  You should have received a copy of the Apache-2.0 license                */
21   	/*  along with SCIP; see the file LICENSE. If not visit scipopt.org.         */
22   	/*                                                                           */
23   	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24   	
25   	/**@file   syncstore.c
26   	 * @ingroup PARALLEL
27   	 * @brief  the function definitions of the synchronization store
28   	 * @author Leona Gottwald
29   	 * @author Stephen J. Maher
30   	 * @author Marc Pfetsch
31   	 */
32   	
33   	/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
34   	
35   	#include <assert.h>
36   	
37   	#include "scip/def.h"
38   	#include "scip/pub_message.h"
39   	#include "scip/concsolver.h"
40   	#include "scip/struct_concsolver.h"
41   	#include "scip/prob.h"
42   	#include "scip/scip.h"
43   	#include "blockmemshell/memory.h"
44   	#include "tpi/tpi.h"
45   	#include "scip/struct_syncstore.h"
46   	#include "scip/concurrent.h"
47   	#include "scip/syncstore.h"
48   	#include "scip/boundstore.h"
49   	
50   	
51   	/** computes the size of the array of synchronization datas, such that
52   	 *  it cannot ever happen that a synchronization data is reused while still
53   	 *  not read by any thread */
54   	static
55   	int getNSyncdata(
56   	   SCIP*                 scip                /**< SCIP main datastructure */
57   	   )
58   	{
59   	   int maxnsyncdelay;
60   	
61   	   SCIP_CALL_ABORT( SCIPgetIntParam(scip, "concurrent/sync/maxnsyncdelay", &maxnsyncdelay) );
62   	
63   	   return 2 * (maxnsyncdelay + 1);
64   	}
65   	
66   	/** creates and captures a new synchronization store */
67   	SCIP_RETCODE SCIPsyncstoreCreate(
68   	   SCIP_SYNCSTORE**      syncstore           /**< pointer to return the created synchronization store */
69   	   )
70   	{
71   	   assert(syncstore != NULL);
72   	
73   	   SCIPdebugMessage("SCIPsyncstoreCreate()\n");
74   	
75   	   SCIP_ALLOC( BMSallocMemory(syncstore) );
76   	
77   	   (*syncstore)->mode = SCIP_PARA_DETERMINISTIC;                      /* initialising the mode */
78   	   (*syncstore)->initialized = FALSE;
79   	   (*syncstore)->syncdata = NULL;
80   	   (*syncstore)->stopped = FALSE;
81   	   (*syncstore)->nuses = 1;
82   	
83   	   SCIP_CALL( SCIPtpiInitLock(&(*syncstore)->lock) );
84   	
85   	   return SCIP_OKAY;
86   	}
87   	
88   	/** releases a synchronization store */
89   	SCIP_RETCODE SCIPsyncstoreRelease(
90   	   SCIP_SYNCSTORE**      syncstore           /**< pointer to the synchronization store */
91   	   )
92   	{
93   	   int references;
94   	
95   	   assert(syncstore != NULL);
96   	   if( *syncstore == NULL )
97   	      return SCIP_OKAY;
98   	
99   	   SCIP_CALL( SCIPtpiAcquireLock((*syncstore)->lock) );
100  	   (*syncstore)->nuses -= 1;
101  	   references = (*syncstore)->nuses;
102  	   SCIP_CALL( SCIPtpiReleaseLock((*syncstore)->lock) );
103  	
104  	   if( references == 0 )
105  	   {
106  	      if( (*syncstore)->initialized )
107  	      {
108  	         SCIP_CALL( SCIPsyncstoreExit(*syncstore) );
109  	      }
110  	
111  	      assert(!(*syncstore)->initialized);
112  	      SCIPtpiDestroyLock(&(*syncstore)->lock);
113  	      BMSfreeMemory(syncstore);
114  	   }
115  	   else
116  	   {
117  	      *syncstore = NULL;
118  	   }
119  	
120  	   return SCIP_OKAY;
121  	}
122  	
123  	/** captures a synchronization store */
124  	SCIP_RETCODE SCIPsyncstoreCapture(
125  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
126  	   )
127  	{
128  	   SCIP_CALL( SCIPtpiAcquireLock(syncstore->lock) );
129  	
130  	   ++(syncstore->nuses);
131  	
132  	   SCIP_CALL( SCIPtpiReleaseLock(syncstore->lock) );
133  	
134  	   return SCIP_OKAY;
135  	}
136  	
137  	/** initialize the syncstore for the given SCIP instance */
138  	SCIP_RETCODE SCIPsyncstoreInit(
139  	   SCIP*                 scip                /**< SCIP main datastructure */
140  	   )
141  	{
142  	   SCIP_SYNCSTORE* syncstore;
143  	   int i;
144  	   int j;
145  	   int paramode;
146  	
147  	   assert(scip != NULL);
148  	   syncstore = SCIPgetSyncstore(scip);
149  	   assert(syncstore != NULL);
150  	   syncstore->mainscip = scip;
151  	   syncstore->lastsync = NULL;
152  	   syncstore->nsolvers = SCIPgetNConcurrentSolvers(scip);
153  	
154  	   syncstore->ninitvars = SCIPgetNVars(scip);
155  	   SCIP_CALL( SCIPgetIntParam(scip, "concurrent/sync/maxnsols", &syncstore->maxnsols) );
156  	   SCIP_CALL( SCIPgetIntParam(scip, "concurrent/sync/maxnsyncdelay", &syncstore->maxnsyncdelay) );
157  	   SCIP_CALL( SCIPgetRealParam(scip, "concurrent/sync/minsyncdelay", &syncstore->minsyncdelay) );
158  	   SCIP_CALL( SCIPgetRealParam(scip, "concurrent/sync/freqinit", &syncstore->syncfreqinit) );
159  	   SCIP_CALL( SCIPgetRealParam(scip, "concurrent/sync/freqmax", &syncstore->syncfreqmax) );
160  	   syncstore->nsyncdata = getNSyncdata(scip);
161  	   SCIP_CALL( SCIPallocBlockMemoryArray(syncstore->mainscip, &(syncstore->syncdata), syncstore->nsyncdata) );
162  	
163  	   for( i = 0; i < syncstore->nsyncdata; ++i )
164  	   {
165  	      syncstore->syncdata[i].syncnum = -1;
166  	      SCIP_CALL( SCIPboundstoreCreate(syncstore->mainscip, &syncstore->syncdata[i].boundstore, syncstore->ninitvars) );
167  	      SCIP_CALL( SCIPallocBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].solobj, syncstore->maxnsols) );
168  	      SCIP_CALL( SCIPallocBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].solsource, syncstore->maxnsols) );
169  	      SCIP_CALL( SCIPallocBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].sols, syncstore->maxnsols) );
170  	
171  	      for( j = 0; j < syncstore->maxnsols; ++j )
172  	      {
173  	         SCIP_CALL( SCIPallocBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].sols[j], syncstore->ninitvars) );
174  	      }
175  	
176  	      SCIP_CALL( SCIPtpiInitLock(&(syncstore->syncdata[i].lock)) );
177  	      SCIP_CALL( SCIPtpiInitCondition(&(syncstore->syncdata[i].allsynced)) );
178  	   }
179  	
180  	   syncstore->initialized = TRUE;
181  	   syncstore->stopped = FALSE;
182  	
183  	   SCIP_CALL( SCIPgetIntParam(scip, "parallel/mode", &paramode) );
184  	   syncstore->mode = (SCIP_PARALLELMODE) paramode;
185  	
186  	   SCIP_CALL( SCIPtpiInit(syncstore->nsolvers, INT_MAX, FALSE) );
187  	   SCIP_CALL( SCIPautoselectDisps(scip) );
188  	
189  	   if( syncstore->mode == SCIP_PARA_DETERMINISTIC )
190  	   {
191  	      /* in deterministic mode use the number of non-zeros and the number of variables to get a good
192  	       * syncdelay and maximum syncfreq
193  	       */
194  	      syncstore->minsyncdelay *= 0.01 * (SCIPgetNNZs(scip) * SCIPgetNVars(scip)); /*lint !e790*/
195  	      syncstore->syncfreqmax *= 0.01 * (SCIPgetNNZs(scip) * SCIPgetNVars(scip));  /*lint !e790*/
196  	   }
197  	
198  	   return SCIP_OKAY;
199  	}
200  	
201  	/** deinitializes the synchronization store */
202  	SCIP_RETCODE SCIPsyncstoreExit(
203  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
204  	   )
205  	{
206  	   int i;
207  	   int j;
208  	
209  	   assert(syncstore != NULL);
210  	   assert(syncstore->initialized);
211  	
212  	   SCIP_CALL( SCIPtpiExit() );
213  	
214  	   for( i = 0; i < syncstore->nsyncdata; ++i )
215  	   {
216  	      SCIPtpiDestroyLock(&(syncstore->syncdata[i].lock));
217  	      SCIPtpiDestroyCondition(&(syncstore->syncdata[i].allsynced));
218  	      SCIPfreeBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].solobj, syncstore->maxnsols);
219  	      SCIPfreeBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].solsource, syncstore->maxnsols);
220  	      SCIPboundstoreFree(syncstore->mainscip,  &syncstore->syncdata[i].boundstore);
221  	
222  	      for( j = 0; j < syncstore->maxnsols; ++j )
223  	      {
224  	         SCIPfreeBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].sols[j], syncstore->ninitvars);
225  	      }
226  	
227  	      SCIPfreeBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata[i].sols, syncstore->maxnsols);
228  	   }
229  	
230  	   SCIPfreeBlockMemoryArray(syncstore->mainscip, &syncstore->syncdata, syncstore->nsyncdata);
231  	
232  	   syncstore->initialized = FALSE;
233  	   syncstore->stopped = FALSE;
234  	
235  	   return SCIP_OKAY;
236  	}
237  	
238  	/** checks whether the solve-is-stopped flag in the syncstore has been set by any thread */
239  	SCIP_Bool SCIPsyncstoreSolveIsStopped(
240  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
241  	   )
242  	{
243  	   SCIP_Bool stopped;
244  	
245  	   SCIP_CALL_ABORT( SCIPtpiAcquireLock(syncstore->lock) );
246  	
247  	   stopped = syncstore->stopped;
248  	
249  	   SCIP_CALL_ABORT( SCIPtpiReleaseLock(syncstore->lock) );
250  	
251  	   return stopped;
252  	}
253  	
254  	/** sets the solve-is-stopped flag in the syncstore so that subsequent calls to
255  	 *  SCIPsyncstoreSolveIsStopped will return the given value in any thread
256  	 */
257  	void SCIPsyncstoreSetSolveIsStopped(
258  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
259  	   SCIP_Bool             stopped             /**< flag if the solve is stopped */
260  	   )
261  	{
262  	   SCIP_CALL_ABORT( SCIPtpiAcquireLock(syncstore->lock) );
263  	
264  	   syncstore->stopped = stopped;
265  	
266  	   SCIP_CALL_ABORT( SCIPtpiReleaseLock(syncstore->lock) );
267  	}
268  	
269  	/** gets the upperbound from the last synchronization */
270  	SCIP_Real SCIPsyncstoreGetLastUpperbound(
271  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
272  	   )
273  	{
274  	   assert(syncstore != NULL);
275  	   assert(syncstore->initialized);
276  	
277  	   return syncstore->lastsync == NULL ? SCIPinfinity(syncstore->mainscip) : syncstore->lastsync->bestupperbound;
278  	}
279  	
280  	/** gets the lowerbound from the last synchronization */
281  	SCIP_Real SCIPsyncstoreGetLastLowerbound(
282  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
283  	   )
284  	{
285  	   assert(syncstore != NULL);
286  	   assert(syncstore->initialized);
287  	
288  	   return syncstore->lastsync == NULL ? -SCIPinfinity(syncstore->mainscip) : syncstore->lastsync->bestlowerbound;
289  	}
290  	
291  	/** gets the number of solutions from the last synchronization */
292  	int SCIPsyncstoreGetLastNSols(
293  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
294  	   )
295  	{
296  	   assert(syncstore != NULL);
297  	   assert(syncstore->initialized);
298  	
299  	   return syncstore->lastsync == NULL ? 0 : syncstore->lastsync->nsols;
300  	}
301  	
302  	/** gets the number of boundchanges from the last synchronization */
303  	int SCIPsyncstoreGetLastNBounds(
304  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
305  	   )
306  	{
307  	   assert(syncstore != NULL);
308  	   assert(syncstore->initialized);
309  	
310  	   return syncstore->lastsync == NULL ? 0 : SCIPboundstoreGetNChgs(syncstore->lastsync->boundstore);
311  	}
312  	
313  	/** gets total memory used by all solvers from the last synchronization */
314  	SCIP_Longint SCIPsyncstoreGetLastMemTotal(
315  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
316  	   )
317  	{
318  	   assert(syncstore != NULL);
319  	   assert(syncstore->initialized);
320  	
321  	   return syncstore->lastsync == NULL ? 0 : syncstore->lastsync->memtotal;
322  	}
323  	
324  	/** gets the synchronization frequency from the last synchronization */
325  	SCIP_Real SCIPsyncstoreGetLastSyncfreq(
326  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
327  	   )
328  	{
329  	   assert(syncstore != NULL);
330  	   assert(syncstore->initialized);
331  	
332  	   return syncstore->lastsync == NULL ? 0.0 : syncstore->lastsync->syncfreq;
333  	}
334  	
335  	/** get synchronization data with given number. It is the responsibility of the caller
336  	 *  to only ask for a synchronization number that still exists, which is checked
337  	 *  with an assert in debug mode. */
338  	SCIP_SYNCDATA* SCIPsyncstoreGetSyncdata(
339  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
340  	   SCIP_Longint          syncnum             /**< the number of the synchronization to start, which
341  	                                              *   must be increasing between calls of the same thread */
342  	   )
343  	{
344  	   int j;
345  	
346  	   assert(syncstore != NULL);
347  	   assert(syncstore->initialized);
348  	
349  	   j = (int) syncnum % syncstore->nsyncdata;
350  	
351  	   /* check if requested syncnumber still exists if in debug mode */
352  	   assert(syncstore->syncdata[j].syncnum == syncnum);
353  	
354  	   return &syncstore->syncdata[j];
355  	}
356  	
357  	/** get the next synchronization data that should be read and
358  	 *  adjust the delay. Returns NULL if no more data should be read due to minimum delay */
359  	SCIP_SYNCDATA* SCIPsyncstoreGetNextSyncdata(
360  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
361  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data */
362  	   SCIP_Real             syncfreq,           /**< the current synchronization frequency */
363  	   SCIP_Longint          writenum,           /**< number of synchronizations the solver has written to */
364  	   SCIP_Real*            delay               /**< pointer holding the current synchronization delay */
365  	   )
366  	{
367  	   SCIP_Real newdelay;
368  	   SCIP_Longint nextsyncnum;
369  	
370  	   assert(syncstore != NULL);
371  	   assert(syncstore->initialized);
372  	   assert(delay != NULL);
373  	
374  	   if( syncdata == NULL )
375  	   {
376  	      nextsyncnum = 0;
377  	   }
378  	   else
379  	   {
380  	      if( syncdata->status != SCIP_STATUS_UNKNOWN )
381  	         return NULL;
382  	
383  	      nextsyncnum = syncdata->syncnum + 1;
384  	   }
385  	
386  	   if( nextsyncnum == writenum )
387  	      return NULL;
388  	
389  	   newdelay = *delay - syncfreq;
390  	
391  	   /* if the delay would get too small we dont want to read the next syncdata.
392  	    * But due to the limited length of the syncdata array we might need to
393  	    * read this synchronization data anyways which is checked by the second part
394  	    * of the if condition
395  	    */
396  	   if( newdelay < syncstore->minsyncdelay && nextsyncnum >= writenum - syncstore->maxnsyncdelay )
397  	      return NULL;
398  	
399  	   *delay = newdelay;
400  	   assert(syncstore->syncdata[nextsyncnum % syncstore->nsyncdata].syncnum == nextsyncnum);
401  	
402  	   return &syncstore->syncdata[nextsyncnum % syncstore->nsyncdata];
403  	}
404  	
405  	/** ensures that the given synchronization data has been written by
406  	 *  all solvers upon return of this function and blocks the caller if necessary. */
407  	SCIP_RETCODE SCIPsyncstoreEnsureAllSynced(
408  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
409  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
410  	   )
411  	{
412  	   assert(syncdata != NULL);
413  	   assert(syncstore != NULL);
414  	   assert(syncstore->initialized);
415  	
416  	   /* check if waiting is required, make sure to hold the lock */
417  	   SCIP_CALL( SCIPtpiAcquireLock(syncdata->lock) );
418  	
419  	   while( syncdata->syncedcount < syncstore->nsolvers )
420  	   {
421  	      /* yes, so wait on the condition variable
422  	       * (automatically releases the lock and reacquires it after the waiting)
423  	       */
424  	      SCIP_CALL( SCIPtpiWaitCondition(syncdata->allsynced, syncdata->lock) );
425  	   }
426  	
427  	   SCIP_CALL( SCIPtpiReleaseLock(syncdata->lock) );
428  	
429  	   return SCIP_OKAY;
430  	}
431  	
432  	/** Start synchronization for the given concurrent solver.
433  	 *  Needs to be followed by a call to SCIPsyncstoreFinishSync if
434  	 *  the syncdata that is returned is not NULL
435  	 */
436  	SCIP_RETCODE SCIPsyncstoreStartSync(
437  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
438  	   SCIP_Longint          syncnum,            /**< the number of the synchronization to start, which
439  	                                              *   must be increasing between calls of the same thread */
440  	   SCIP_SYNCDATA**       syncdata            /**< pointer to return the synchronization data */
441  	   )
442  	{
443  	   int i;
444  	
445  	   assert(syncdata != NULL);
446  	   assert(syncstore != NULL);
447  	   assert(syncstore->initialized);
448  	
449  	   if( SCIPsyncstoreSolveIsStopped(syncstore) )
450  	   {
451  	      *syncdata = NULL;
452  	      return SCIP_OKAY;
453  	   }
454  	
455  	   i = syncnum % syncstore->nsyncdata; /*lint !e712*/
456  	   *syncdata = &syncstore->syncdata[i];
457  	   assert(*syncdata != NULL);
458  	
459  	   SCIP_CALL( SCIPtpiAcquireLock((*syncdata)->lock) );
460  	
461  	   if( (*syncdata)->syncnum != syncnum )
462  	   {
463  	      SCIPboundstoreClear((*syncdata)->boundstore);
464  	      (*syncdata)->nsols = 0;
465  	      (*syncdata)->memtotal = SCIPgetMemTotal(syncstore->mainscip);
466  	      (*syncdata)->syncedcount = 0;
467  	      (*syncdata)->bestupperbound = SCIPinfinity(syncstore->mainscip);
468  	      (*syncdata)->bestlowerbound = -(*syncdata)->bestupperbound;
469  	      (*syncdata)->status = SCIP_STATUS_UNKNOWN;
470  	      (*syncdata)->winner = 0;
471  	      (*syncdata)->syncnum = syncnum;
472  	      (*syncdata)->syncfreq = 0.0;
473  	   }
474  	
475  	   return SCIP_OKAY;
476  	}
477  	
478  	/** finishes synchronization for the synchronization data */
479  	SCIP_RETCODE SCIPsyncstoreFinishSync(
480  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
481  	   SCIP_SYNCDATA**       syncdata            /**< the synchronization data */
482  	   )
483  	{
484  	   SCIP_Bool printline = FALSE;
485  	
486  	   assert(syncdata != NULL);
487  	   assert((*syncdata) != NULL);
488  	   assert(syncstore != NULL);
489  	   assert(syncstore->initialized);
490  	
491  	   ++(*syncdata)->syncedcount;
492  	
493  	   if( (*syncdata)->syncedcount == syncstore->nsolvers )
494  	   {
495  	      if( (*syncdata)->status != SCIP_STATUS_UNKNOWN )
496  	         SCIPsyncstoreSetSolveIsStopped(syncstore, TRUE);
497  	
498  	      syncstore->lastsync = *syncdata;
499  	      printline = TRUE;
500  	
501  	      SCIP_CALL( SCIPtpiBroadcastCondition((*syncdata)->allsynced) );
502  	   }
503  	
504  	   SCIP_CALL( SCIPtpiReleaseLock((*syncdata)->lock) );
505  	
506  	   if( printline )
507  	   {
508  	      SCIP_CALL( SCIPprintDisplayLine(syncstore->mainscip, NULL, SCIP_VERBLEVEL_HIGH, TRUE) );
509  	   }
510  	
511  	   *syncdata = NULL;
512  	
513  	   return SCIP_OKAY;
514  	}
515  	
516  	/** gets status in synchronization data */
517  	SCIP_STATUS SCIPsyncdataGetStatus(
518  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
519  	   )
520  	{
521  	   assert(syncdata != NULL);
522  	
523  	   return syncdata->status;
524  	}
525  	
526  	/** gets the solver that had the best status, or -1 if solve is not stopped yet */
527  	int SCIPsyncstoreGetWinner(
528  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
529  	   )
530  	{
531  	   assert(syncstore != NULL);
532  	   assert(syncstore->initialized);
533  	
534  	   if( syncstore->lastsync == NULL || syncstore->lastsync->status == SCIP_STATUS_UNKNOWN )
535  	      return -1;
536  	
537  	   return syncstore->lastsync->winner;
538  	}
539  	
540  	/** how many solvers have already finished synchronizing on this sychronization data */
541  	int SCIPsyncdataGetNSynced(
542  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
543  	   )
544  	{
545  	   assert(syncdata != NULL);
546  	
547  	   return syncdata->syncedcount;
548  	}
549  	
550  	/** how many solvers have are running concurrently */
551  	int SCIPsyncstoreGetNSolvers(
552  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
553  	   )
554  	{
555  	   assert(syncstore != NULL);
556  	   assert(syncstore->initialized);
557  	
558  	   return syncstore->nsolvers;
559  	}
560  	
561  	/** read amount total memory used from synchronization data */
562  	SCIP_Longint SCIPsyncdataGetMemTotal(
563  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
564  	   )
565  	{
566  	   assert(syncdata != NULL);
567  	
568  	   return syncdata->memtotal;
569  	}
570  	
571  	/** read the synchronization frequency from a synchronization data */
572  	SCIP_Real SCIPsyncdataGetSyncFreq(
573  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
574  	   )
575  	{
576  	   assert(syncdata != NULL);
577  	
578  	   return syncdata->syncfreq;
579  	}
580  	
581  	/** read the upperbound stored in a synchronization data */
582  	SCIP_Real SCIPsyncdataGetUpperbound(
583  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
584  	   )
585  	{
586  	   assert(syncdata != NULL);
587  	
588  	   return syncdata->bestupperbound;
589  	}
590  	
591  	/** read the lowerbound stored in a synchronization data */
592  	SCIP_Real SCIPsyncdataGetLowerbound(
593  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
594  	   )
595  	{
596  	   assert(syncdata != NULL);
597  	
598  	   return syncdata->bestlowerbound;
599  	}
600  	
601  	/** read the solutions stored in a synchronization data */
602  	void SCIPsyncdataGetSolutions(
603  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data */
604  	   SCIP_Real***          solvalues,          /**< array of buffers containing the solution values */
605  	   int**                 solowner,           /**< array of ownerids of solutions */
606  	   int*                  nsols               /**< pointer to return number of solutions */
607  	   )
608  	{
609  	   assert(syncdata != NULL);
610  	   assert(solvalues != NULL);
611  	   assert(solowner != NULL);
612  	   assert(nsols != NULL);
613  	
614  	   *solvalues = syncdata->sols;
615  	   *solowner = syncdata->solsource;
616  	   *nsols = syncdata->nsols;
617  	}
618  	
619  	/** read bound changes stored in the synchronization data */
620  	SCIP_BOUNDSTORE* SCIPsyncdataGetBoundChgs(
621  	   SCIP_SYNCDATA*        syncdata            /**< the synchronization data */
622  	   )
623  	{
624  	   assert(syncdata != NULL);
625  	
626  	   return syncdata->boundstore;
627  	}
628  	
629  	/** write the synchronization frequency to a synchronization data */
630  	void SCIPsyncdataSetSyncFreq(
631  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
632  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data */
633  	   SCIP_Real             syncfreq            /**< the synchronization frequency */
634  	   )
635  	{
636  	   assert(syncstore != NULL);
637  	   assert(syncstore->initialized);
638  	   assert(syncdata != NULL);
639  	
640  	   syncdata->syncfreq = MIN(syncfreq, syncstore->syncfreqmax);
641  	}
642  	
643  	/** set status in the synchronization data */
644  	void SCIPsyncdataSetStatus(
645  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data the upperbound should be added to */
646  	   SCIP_STATUS           status,             /**< the status */
647  	   int                   solverid            /**< identifier of te solver that has this status */
648  	   )
649  	{
650  	   assert(syncdata != NULL);
651  	
652  	   /* check if status is better than current one (closer to SCIP_STATUS_OPTIMAL),
653  	    * break ties by the solverid, and remember the solver wit the best status
654  	    * so that the winner will be selected deterministically
655  	    */
656  	   if( syncdata->status < SCIP_STATUS_OPTIMAL )
657  	   {
658  	      if( status > syncdata->status || (status == syncdata->status && solverid < syncdata->winner) )
659  	      {
660  	         syncdata->status = status;
661  	         syncdata->winner = solverid;
662  	      }
663  	   }
664  	   else if( syncdata->status > SCIP_STATUS_OPTIMAL && status >= SCIP_STATUS_OPTIMAL )
665  	   {
666  	      if( status < syncdata->status || (status == syncdata->status && solverid < syncdata->winner) )
667  	      {
668  	         syncdata->status = status;
669  	         syncdata->winner = solverid;
670  	      }
671  	   }
672  	   else if( syncdata->winner < 0 )
673  	   {
674  	      syncdata->status = status;
675  	      syncdata->winner = solverid;
676  	   }
677  	}
678  	
679  	/** adds memory used to the synchronization data */
680  	void SCIPsyncdataAddMemTotal(
681  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data the solution should be added to */
682  	   SCIP_Longint          memtotal            /**< the number of bytes used */
683  	   )
684  	{
685  	   assert(syncdata != NULL);
686  	
687  	   syncdata->memtotal += memtotal;
688  	}
689  	
690  	/** set upperbound to the synchronization data */
691  	void SCIPsyncdataSetUpperbound(
692  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data the upperbound should be added to */
693  	   SCIP_Real             upperbound          /**< the upperbound */
694  	   )
695  	{
696  	   assert(syncdata != NULL);
697  	
698  	   syncdata->bestupperbound = MIN(syncdata->bestupperbound, upperbound);
699  	}
700  	
701  	/** set lowerbound to the synchronization data */
702  	void SCIPsyncdataSetLowerbound(
703  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data the lowerbound should be added to */
704  	   SCIP_Real             lowerbound          /**< the lowerbound */
705  	   )
706  	{
707  	   assert(syncdata != NULL);
708  	
709  	   syncdata->bestlowerbound = MAX(syncdata->bestlowerbound, lowerbound);
710  	}
711  	
712  	/** gives a buffer to store the solution values, or NULL if solution should not be stored
713  	 *  because there are already better solutions stored.
714  	 */
715  	void SCIPsyncdataGetSolutionBuffer(
716  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
717  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data the solution should be added to */
718  	   SCIP_Real             solobj,             /**< the objective value of the solution */
719  	   int                   ownerid,            /**< an identifier for the owner of the solution, e.g. the thread number */
720  	   SCIP_Real**           buffer              /**< pointer to return a buffer for the solution values, which must be set
721  	                                              *   if the buffer is not NULL */
722  	   )
723  	{
724  	   int pos;
725  	   int i;
726  	
727  	   assert(syncstore != NULL);
728  	   assert(syncstore->initialized);
729  	   assert(syncdata != NULL);
730  	   assert(buffer != NULL);
731  	
732  	   for( pos = 0; pos < syncdata->nsols; ++pos )
733  	   {
734  	      if( syncdata->solobj[pos] < solobj || (syncdata->solobj[pos] == solobj && ownerid < syncdata->solsource[pos]) ) /*lint !e777*/
735  	         break;
736  	   }
737  	
738  	   if( syncdata->nsols < syncstore->maxnsols )
739  	   {
740  	      for( i = syncdata->nsols; i > pos; --i )
741  	      {
742  	         syncdata->solobj[i] = syncdata->solobj[i - 1];
743  	         syncdata->solsource[i] = syncdata->solsource[i - 1];
744  	         SCIPswapPointers((void**) &syncdata->sols[i], (void**) &syncdata->sols[i - 1]);
745  	      }
746  	
747  	      ++syncdata->nsols;
748  	   }
749  	   else
750  	   {
751  	      --pos;
752  	
753  	      for( i = 0; i < pos; ++i )
754  	      {
755  	         syncdata->solobj[i] = syncdata->solobj[i + 1];
756  	         syncdata->solsource[i] = syncdata->solsource[i + 1];
757  	         SCIPswapPointers((void**) &syncdata->sols[i], (void**) &syncdata->sols[i + 1]);
758  	      }
759  	   }
760  	
761  	   if( pos >= 0 )
762  	   {
763  	      syncdata->solobj[pos] = solobj;
764  	      syncdata->solsource[pos] = ownerid;
765  	      *buffer = syncdata->sols[pos];
766  	   }
767  	   else
768  	   {
769  	      *buffer = NULL;
770  	   }
771  	}
772  	
773  	/** adds bound changes to the synchronization data */
774  	SCIP_RETCODE SCIPsyncdataAddBoundChanges(
775  	   SCIP_SYNCSTORE*       syncstore,          /**< the synchronization store */
776  	   SCIP_SYNCDATA*        syncdata,           /**< the synchronization data */
777  	   SCIP_BOUNDSTORE*      boundstore          /**< bound store containing the bounds to add */
778  	   )
779  	{
780  	   assert(syncstore != NULL);
781  	   assert(syncstore->initialized);
782  	   assert(syncdata != NULL);
783  	   assert(boundstore != NULL);
784  	
785  	   SCIP_CALL( SCIPboundstoreMerge(syncstore->mainscip, syncdata->boundstore, boundstore) );
786  	
787  	   return SCIP_OKAY;
788  	}
789  	
790  	/** is synchronization store initialized */
791  	SCIP_Bool SCIPsyncstoreIsInitialized(
792  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
793  	   )
794  	{
795  	   assert(syncstore != NULL);
796  	
797  	   return syncstore->initialized;
798  	}
799  	
800  	/** returns the mode of the synchronization store */
801  	SCIP_PARALLELMODE SCIPsyncstoreGetMode(
802  	   SCIP_SYNCSTORE*       syncstore           /**< the synchronization store */
803  	   )
804  	{
805  	   assert(syncstore != NULL);
806  	
807  	   return syncstore->mode;
808  	}
809