My experience with Linux/GCC

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: My experience with Linux/GCC

Post by mcostalba »

rvida wrote: Contrary to my fears, the threading, locking and signaling stuff wasn't very hard to port. In case someone finds it useful here is my smp.h:

Code: Select all


--- cut -----

#define ThreadProcResult DWORD WINAPI
typedef LPVOID ThreadProcParam;
typedef ThreadProcResult ThreadProc(ThreadProcParam);

inline void start_thread(ThreadProc proc, ThreadProcParam param) {
  DWORD tid;
  CloseHandle(CreateThread(NULL, 0, proc, param, 0, &tid));
}

--- cut -----

I find it useful, thanks!

I missed the

Code: Select all

CloseHandle(CreateThread(
nice idiom, and probably it means we have a small leak in SF upon exiting. Can I ask how you sync on the threads upon termination ?

I mean, when Critter exits, in which way you are able to wait from the main thread that all the other threads are terminated if you don't keep an handle to the threads an so are unable to WaitForSingleObject() ?

Thanks
Marco
rbarreira
Posts: 900
Joined: Tue Apr 27, 2010 3:48 pm

Re: My experience with Linux/GCC

Post by rbarreira »

hgm wrote:Wouldn't it be better to simply define it as a struct/union?

Code: Select all

typedef union {
  int i;
  struct {
    short int eg_value;
    short int mg_value;
  } s;
} ScorePair;
Then you can write score.s.eg_value in stead of eg_value(score), and you can still access it as an int through score.i. Or do you rely on the order in which the two halves are stored in the int?
That seems broken if the structure members get aligned, unless there's any particular reason why that can't happen inside a union.
wgarvin
Posts: 838
Joined: Thu Jul 05, 2007 5:03 pm
Location: British Columbia, Canada

Re: My experience with Linux/GCC

Post by wgarvin »

rbarreira wrote:
hgm wrote:Wouldn't it be better to simply define it as a struct/union?

Code: Select all

typedef union {
  int i;
  struct {
    short int eg_value;
    short int mg_value;
  } s;
} ScorePair;
Then you can write score.s.eg_value in stead of eg_value(score), and you can still access it as an int through score.i. Or do you rely on the order in which the two halves are stored in the int?
That seems broken if the structure members get aligned, unless there's any particular reason why that can't happen inside a union.
Most compilers (including GCC) default to natural alignment for primitive types ("aligned to same size as the type"). So the int is 4 bytes and has 4-byte alignment, and the shorts are 2 bytes and have 2-byte alignment. The union is 4 bytes and has 4-byte alignment.

If worried about weird compilers, you could put a static assert on the size (or you could just use your own user-defined types of known sizes and not worry about it).
User avatar
rvida
Posts: 481
Joined: Thu Apr 16, 2009 12:00 pm
Location: Slovakia, EU

Re: My experience with Linux/GCC

Post by rvida »

mcostalba wrote: I mean, when Critter exits, in which way you are able to wait from the main thread that all the other threads are terminated if you don't keep an handle to the threads an so are unable to WaitForSingleObject() ?

Thanks
Marco
I don't need the thread handle. In destructor of the Engine class I loop indefinitely until all helper threads have their state set to TS_TERMINATED (this is the last thing they do before returning from their idle loop).

Code: Select all

...
  exit_flag = true;
  wake_threads();
  for &#40;int i = 1; i < num_threads; i++) &#123;
    while &#40;threads&#91;i&#93;.state != TS_TERMINATED&#41;;
  &#125;
...
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: My experience with Linux/GCC

Post by mcostalba »

rvida wrote:
mcostalba wrote: I mean, when Critter exits, in which way you are able to wait from the main thread that all the other threads are terminated if you don't keep an handle to the threads an so are unable to WaitForSingleObject() ?

Thanks
Marco
I don't need the thread handle. In destructor of the Engine class I loop indefinitely until all helper threads have their state set to TS_TERMINATED (this is the last thing they do before returning from their idle loop).

Code: Select all

...
  exit_flag = true;
  wake_threads&#40;);
  for &#40;int i = 1; i < num_threads; i++) &#123;
    while &#40;threads&#91;i&#93;.state != TS_TERMINATED&#41;;
  &#125;
...
Yes, this is similar to how is implemented in SF:

Code: Select all

  for &#40;int i = 0; i < MAX_THREADS; i++)
  &#123;
      // Wake up all the slave threads and wait for termination
      if &#40;i != 0&#41;
      &#123;
          threads&#91;i&#93;.do_terminate = true;
          threads&#91;i&#93;.wake_up&#40;);
          while &#40;threads&#91;i&#93;.state != Thread&#58;&#58;TERMINATED&#41; &#123;&#125;
      &#125;
       ....
  &#125;
but i don't like a lot to reinvent the wheel with our home grown Thread::TERMINATED flag and I'd prefer to use the proper way documented for the thread library.

I am wondering what happens if we leave without waiting for thread termination, does the OS / thread library wait for us before to exit() and free the process space ?
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: My experience with Linux/GCC

Post by mcostalba »

mcostalba wrote: I am wondering what happens if we leave without waiting for thread termination, does the OS / thread library wait for us before to exit() and free the process space ?
It seems to work under Windows, but has bad effects with pthreads, so I have hacked up this thread library dependent code to wait for threads to terminate instead of our home grown flag:

Code: Select all

  for &#40;int i = 0; i < MAX_THREADS; i++)
  &#123;
      // Wake up all the slave threads and wait for termination
      if &#40;i != 0&#41;
      &#123;
          threads&#91;i&#93;.do_terminate = true;
          threads&#91;i&#93;.wake_up&#40;);

#if defined&#40;_MSC_VER&#41;
          WaitForSingleObject&#40;threads&#91;i&#93;.handle, 0&#41;;
          CloseHandle&#40;threads&#91;i&#93;.handle&#41;;
#else
          pthread_join&#40;threads&#91;i&#93;.handle, NULL&#41;;
          pthread_detach&#40;threads&#91;i&#93;.handle&#41;;
#endif
      &#125;
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: My experience with Linux/GCC

Post by bob »

mcostalba wrote:
mcostalba wrote: I am wondering what happens if we leave without waiting for thread termination, does the OS / thread library wait for us before to exit() and free the process space ?
It seems to work under Windows, but has bad effects with pthreads, so I have hacked up this thread library dependent code to wait for threads to terminate instead of our home grown flag:

Code: Select all

  for &#40;int i = 0; i < MAX_THREADS; i++)
  &#123;
      // Wake up all the slave threads and wait for termination
      if &#40;i != 0&#41;
      &#123;
          threads&#91;i&#93;.do_terminate = true;
          threads&#91;i&#93;.wake_up&#40;);

#if defined&#40;_MSC_VER&#41;
          WaitForSingleObject&#40;threads&#91;i&#93;.handle, 0&#41;;
          CloseHandle&#40;threads&#91;i&#93;.handle&#41;;
#else
          pthread_join&#40;threads&#91;i&#93;.handle, NULL&#41;;
          pthread_detach&#40;threads&#91;i&#93;.handle&#41;;
#endif
      &#125;
I've never noticed a problem here. Program might not exit cleanly, but nothing bad should happen that I can think of...
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: My experience with Linux/GCC

Post by mcostalba »

bob wrote: I've never noticed a problem here. Program might not exit cleanly, but nothing bad should happen that I can think of...
Yes, I agree, we have never noticed problems too. Anyhow let the thread raising a flag, like TERMINATED, is intrinsically racy given that when the flag is raised the thread is of course still running, this probably has no practical consequence, anyhow because we are very nitpicking ;-), is not the correct way to detect thread termination in the general case.