External Issue: Global variables are not always initialized

20861, "twesterhout", "Global variables are not always initialized", "2022-10-14T16:02:53Z"

Summary of Problem

This issue has been briefly discussed with @bradcray on Gitter, and I promised to create a reproducer. Also pinging @vasslitvinov because he's attempted to reproduce the issue at some point.

The problem is that sometimes, in multi-locale builds, global variables are not initialized which results in segfaults or "attempt to dereference null" errors.

Example output of the program:

1
1
src/error.chpl:40: error: attempt to dereference nil
Stacktrace

sizeAs() at $CHPL_HOME/modules/internal/ChapelDomain.chpl:1952
size() at $CHPL_HOME/modules/internal/ChapelDomain.chpl:1947
size() at $CHPL_HOME/modules/internal/ChapelArray.chpl:1397
foo() at src/error.chpl:20
bar() at src/error.chpl:18
on_fn() at src/error.chpl:55
wrapon_fn() at src/error.chpl:55

Steps to Reproduce

src/error.chpl:

use CTypes;
use AllLocalesBarriers;

config param shouldCrash : bool = true;

var globalDom : domain(1, int);
var globalArr : [globalDom] atomic real;

class GlobalBuffer {
  var dom : domain(1, int);
  var arr : [dom] atomic real;
}

var alternative = new GlobalBuffer();

require "error_header.h";

extern proc bar(procID : c_int, size : c_int);

export proc foo(sendBuf : c_ptr(real), recvBuf : c_ptr(real), count : c_ptr(c_int)) {
  allLocalesBarrier.barrier();

  const n = count.deref():int;
  const indices = 0 ..# n;

  ref dom = if shouldCrash then globalDom else alternative.dom;
  ref arr = if shouldCrash then globalArr else alternative.arr;

  // Grow the buffer if it's not big enough
  if here.id == 0 {
    if n > dom.size then
      dom = {indices};

    // IMPORTANT!
    arr.write(0, memoryOrder.relaxed);
  }

  // Make sure locale 0 has had the chance to resize before proceeding          
  allLocalesBarrier.barrier();
  assert(arr.size >= n);

  // Have all locales atomically add their results to the atomicBuff            
  forall i in indices do
    arr[i].add(sendBuf[i], memoryOrder.relaxed);

  // Make sure all locales have accumulated their contributions                 
  allLocalesBarrier.barrier();

  // Have each locale copy the results out into its buffer                      
  forall i in indices do
    recvBuf[i] = arr[i].read();
}

proc main() {
  coforall loc in Locales do on loc {
    for i in 1 .. 5 {
      writeln(i);
      bar(loc.id:c_int, i:c_int);
    }
  }
}

src/error.c:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

extern void foo(double const *sendBuf, double *recvBuf, int const *count);

void bar(int procID, int size) {
  double *send = malloc(size * sizeof(double));
  double *recv = malloc(size * sizeof(double));
  assert(send != NULL);
  assert(recv != NULL);

  for (int i = 0; i < size; ++i) {
    send[i] = (double)procID;
  }

  foo(send, recv, &size);

  for (int i = 0; i < size; ++i) {
    fprintf(stderr, "%f, ", recv[i]);
  }
  fprintf(stderr, "\n");

  free(send);
  free(recv);
}

src/error_header.h:

#pragma once

void bar(int procID, int size);

Compile command:

chpl -o error -sshouldCrash=true -I src/ src/error.chpl src/error.c

(NOTE: compiling with shouldCrash=false fixes the issue)

Execution command:

./error -nl2

Configuration Information

  • Output of chpl --version: chpl version 1.28.0 pre-release (3d54c9f456)
  • Output of $CHPL_HOME/util/printchplenv --anonymize:
CHPL_TARGET_PLATFORM: linux64
CHPL_TARGET_COMPILER: llvm
CHPL_TARGET_ARCH: x86_64
CHPL_TARGET_CPU: native *
CHPL_LOCALE_MODEL: flat
CHPL_COMM: gasnet *
  CHPL_COMM_SUBSTRATE: smp *
  CHPL_GASNET_SEGMENT: fast
CHPL_TASKS: qthreads
CHPL_LAUNCHER: smp
CHPL_TIMERS: generic
CHPL_UNWIND: bundled *
CHPL_MEM: jemalloc
CHPL_ATOMICS: cstdlib
  CHPL_NETWORK_ATOMICS: none
CHPL_GMP: none *
CHPL_HWLOC: bundled *
CHPL_RE2: none *
CHPL_LLVM: system *
CHPL_AUX_FILESYS: none