In a nutshell, add the following code to main() before the call to f3():

    int status = __cilkrts_set_param("nworkers", "2");
    if (0 != status) {
        // Failed to set the number of Cilk workers
        return status;
    }

Here's the details:

There are three sources of information the Cilk runtime uses to set the number 
of workers.

1) By default the Cilk runtime will query the operating system for the number 
of cores and create a worker for each core. Note that the operating system 
counts each hyperthread as a core.

2) You can set your own default by defining the CILK_NWORKERS environment 
variable.  For example:

    export CILK_NWORKERS=2

3) You can override the default programmatically by calling 
__cilkrts_set_param() before the first spawning function is called. For 
example, place the following call in main():

    __cilkrts_set_param("nworkers", "2");

A "spawning function" is a function that contains a _Cilk_spawn. The Cilk 
runtime will be initialized the first time a spawning function is executed.

For this test to run correctly you must set the number of workers to at least 
2, so the Cilk runtime will create at least one worker thread.

Please note that once the Cilk runtime has been initialized by the entry into 
the first spawning function, the number of workers cannot be changed (and the 
__cilkrts_set_param() call will return a non-zero value to indicate an error 
has occurred.) The first two values are evaluated lazily; the runtime will load 
its default values the first time one of the __cilkrts APIs defined in 
cilk_api.h is called [for example __cilkrts_get_nworkers()], or the first 
spawning function is entered. So for example, the following code will not do 
what you want:

    if (1 == __cilkrts_get_nworkers())
        setenv("CILK_NWORKERS=2");

The call to __cilkrts_get_nworkers() will cause the Cilk runtime to set its 
default. Setting the CILK_NWORKERS environment variable after the 
__cilkrts_get_nworkers() call will be too late.

It is possible to change the number of workers, but to do that you must return 
from all spawning functions and then call __cilkrts_end_cilk() to have the Cilk 
runtime shut down and then call __cilkrts_set_param(). When the Cilk runtime 
starts at the entry to the next spawning function, it will obey the new setting.

   - Barry

-----Original Message-----
From: Thomas Schwinge [mailto:tho...@codesourcery.com] 
Sent: Monday, September 29, 2014 6:54 AM
To: Tannenbaum, Barry M; Iyer, Balaji V; Zamyatin, Igor
Cc: gcc-patches@gcc.gnu.org
Subject: RE: FW: [PATCH] Cilk Keywords (_Cilk_spawn and _Cilk_sync) for C

Hi!

On Mon, 22 Sep 2014 19:21:33 +0000, "Tannenbaum, Barry M" 
<barry.m.tannenb...@intel.com> wrote:
> That's exactly correct.
> 
> There are two ways to implement a work-stealing scheduler. We refer to 
> them as: [...]

Thanks for the explanation.

> Cilk implements Parent Stealing. Since you're running with 1 worker, there's 
> no other worker to steal the continuation. So steal_flag will never be set to 
> 1 and you'll never break out of the loop.

Remains the question about how to address that in the testsuite:

> -----Original Message-----
> From: Thomas Schwinge [mailto:tho...@codesourcery.com]
> Sent: Monday, September 22, 2014 9:56 AM
> To: Iyer, Balaji V
> Cc: gcc-patches@gcc.gnu.org
> Subject: Re: FW: [PATCH] Cilk Keywords (_Cilk_spawn and _Cilk_sync) 
> for C
> 
> Hi!
> 
> On Tue, 27 Aug 2013 21:30:49 +0000, "Iyer, Balaji V" 
> <balaji.v.i...@intel.com> wrote:
> > --- /dev/null
> > +++ gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
> > @@ -0,0 +1,37 @@
> > +/* { dg-do run  { target { i?86-*-* x86_64-*-* arm*-*-* } } } */
> > +/* { dg-options "-fcilkplus" } */
> > +/* { dg-options "-lcilkrts" { target { i?86-*-* x86_64-*-* arm*-*-* 
> > +} } } */
> > +
> > +void f0(volatile int *steal_flag)
> > +{
> > +  int i = 0;
> > +  /* Wait for steal_flag to be set */
> > +  while (!*steal_flag) 
> > +    ;
> > +}
> > +
> > +int f1()
> > +{
> > +
> > +  volatile int steal_flag = 0;
> > +  _Cilk_spawn f0(&steal_flag);
> > +  steal_flag = 1;  // Indicate stolen
> > +  _Cilk_sync;
> > +  return 0;
> > +}
> > +
> > +void f2(int q)
> > +{
> > +  q = 5;
> > +}
> > +
> > +void f3()
> > +{
> > +   _Cilk_spawn f2(f1());
> > +}
> > +
> > +int main()
> > +{
> > +  f3();
> > +  return 0;
> > +}
> 
> Is this really well-formed Cilk Plus code?  Running with CILK_NWORKERS=1, or 
> -- the equivalent -- in a system with just one CPU (as per 
> libcilkrts/runtime/os-unix.c:__cilkrts_hardware_cpu_count returning 1), I see 
> this test busy-loop as follows:
> 
>     Breakpoint 1, __cilkrts_hardware_cpu_count () at 
> ../../../source/libcilkrts/runtime/os-unix.c:358
>     358     {
>     (gdb) return 1
>     Make __cilkrts_hardware_cpu_count return now? (y or n) y
>     #0  cilkg_get_user_settable_values () at 
> ../../../source/libcilkrts/runtime/global_state.cpp:385
>     385             CILK_ASSERT(hardware_cpu_count > 0);
>     (gdb) c
>     Continuing.
>     ^C
>     Program received signal SIGINT, Interrupt.
>     f0 (steal_flag=steal_flag@entry=0x7fffffffd03c) at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     9         while (!*steal_flag) 
>     (gdb) info threads
>       Id   Target Id         Frame 
>     * 1    Thread 0x7ffff7fcd780 (LWP 30816) "spawning_arg.ex" f0 
> (steal_flag=steal_flag@entry=0x7fffffffd03c) at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     (gdb) list
>     4
>     5       void f0(volatile int *steal_flag)
>     6       { 
>     7         int i = 0;
>     8         /* Wait for steal_flag to be set */
>     9         while (!*steal_flag) 
>     10          ;
>     11      }
>     12
>     13      int f1()
>     (gdb) bt
>     #0  f0 (steal_flag=steal_flag@entry=0x7fffffffd03c) at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:9
>     #1  0x00000000004009c8 in _cilk_spn_0 () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:17
>     #2  0x0000000000400a4b in f1 () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:17
>     #3  0x0000000000400d0e in _cilk_spn_1 () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:30
>     #4  0x0000000000400d7a in f3 () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:30
>     #5  0x0000000000400e33 in main () at 
> [...]/source/gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c:35
> 
> No additional thread has been spawned by libcilkrts, and the one initial 
> thread is stuck in f0, without being able to make progress.  Should in f0's 
> while loop, some function be called to "yield to libcilkrts scheduler", or 
> should libcilkrts have spawned an additional thread, or is the test case just 
> not valid Cilk Plus code?

Assuming the test cases are considered well-formed Cilk Plus code, I understand 
there is then a hard requirement to run them with more than one worker.  OK to 
fix as follows?

commit ee7138e451d1f3284d6fa0f61fe517c82db94060
Author: Thomas Schwinge <tho...@codesourcery.com>
Date:   Mon Sep 29 12:47:34 2014 +0200

    Audit Cilk Plus tests for CILK_NWORKERS=1.
    
        gcc/testsuite/
        * c-c++-common/cilk-plus/CK/spawning_arg.c (main): Call
        __cilkrts_set_param to set two workers.
        * c-c++-common/cilk-plus/CK/steal_check.c (main): Likewise.
        * g++.dg/cilk-plus/CK/catch_exc.cc (main): Likewise.
---
 gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c | 15 +++++++++++++++  
gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c  | 17 ++++++++++++++---
 gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc         | 14 ++++++++++++++
 3 files changed, 43 insertions(+), 3 deletions(-)

diff --git gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c 
gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
index 95e6cab..138b82c 100644
--- gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
+++ gcc/testsuite/c-c++-common/cilk-plus/CK/spawning_arg.c
@@ -2,6 +2,17 @@
 /* { dg-options "-fcilkplus" } */
 /* { dg-additional-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+
 void f0(volatile int *steal_flag)
 { 
   int i = 0;
@@ -32,6 +43,10 @@ void f3()
 
 int main()
 {
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   f3();
   return 0;
 }
diff --git gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c 
gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
index 6e28765..6b41c7f 100644
--- gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
+++ gcc/testsuite/c-c++-common/cilk-plus/CK/steal_check.c
@@ -2,8 +2,16 @@
 /* { dg-options "-fcilkplus" } */
 /* { dg-additional-options "-lcilkrts" { target { i?86-*-* x86_64-*-* } } } */
 
-// #include <cilk/cilk_api.h>
-extern void __cilkrts_set_param (char *, char *);
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
 
 void foo(volatile int *);
 
@@ -11,7 +19,10 @@ void main2(void);
 
 int main(void)
 {
- //  __cilkrts_set_param ((char *)"nworkers", (char *)"2");
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   main2();
   return 0;
 }
diff --git gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc 
gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
index 0633d19..09ddf8b 100644
--- gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
+++ gcc/testsuite/g++.dg/cilk-plus/CK/catch_exc.cc
@@ -10,6 +10,16 @@
 #endif
 #include <cstdlib>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int __cilkrts_set_param (const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
 
 void func(int volatile* steal_me)
 {
@@ -59,6 +69,10 @@ void my_test()
 
 int main()
 {
+  /* Ensure more than one worker.  */
+  if (__cilkrts_set_param("nworkers", "2") != 0)
+    __builtin_abort();
+
   my_test();
 #if HAVE_IO
   printf("PASSED\n");


Grüße,
 Thomas

Reply via email to