1

我从事开源项目http://gtkworkbook.sourceforge.net/已经有一段时间了,最​​近遇到了一个问题,好像我在绕圈子。我很确定存在堆问题,但我查看这段代码太久了,无法弄清楚它到底是什么。

所以,简而言之,我正在做的是在使用 libcs​​v 解析器时将一块内存从 N 指针重新分配到 M 指针。如果还有其他列,我想将数组的最大大小增加到当前大小的 2 倍。这是当前的代码:


 struct csv_column {
    Sheet * sheet;
    Cell ** array;
    int & array_max;
    int & array_size;
    int row;
    int field;
    char * value;
  };

  static void 
  cb1 (void * s, size_t length, void * data) {
    struct csv_column * column = (struct csv_column *) data;
    int & array_max = column->array_max;

    // Resize the cell array here.
    if (column->field >= array_max) {
      int max = (2 * array_max);
      (column->array) = (Cell **) g_realloc ((column->array), max * sizeof (Cell*));

      for (int ii = array_max; ii array)[column->field] == NULL)
      (column->array)[column->field] = cell_new();

    Cell * cell = (column->array)[column->field];
    cell->set_row (cell, column->row);
    cell->set_column (cell, column->field++);
    cell->set_value_length (cell, s, length);
  }

  CsvParser::CsvParser (Workbook * wb,
            FILE * log,
            int verbosity,
            int maxOfFields) {
    this->wb = wb;
    this->log = log;
    this->verbosity = verbosity;
    this->sizeOfFields = 0;
    this->maxOfFields = maxOfFields;
    this->fields = (Cell **) g_malloc (maxOfFields * sizeof (Cell*));

    for (int ii = 0; ii maxOfFields; ii++)
      this->fields[ii] = NULL;
  }

  CsvParser::~CsvParser (void) {
    for (int ii = 0; ii maxOfFields; ii++) {
      if (this->fields[ii])
        this->fields[ii]->destroy (this->fields[ii]);
    }

    g_free (this->fields);
  }

这是 valgrind 的输出:

==28476== 线程 9:
==28476== 大小为 8 的读取无效
==28476== 在 0x771AF4F: sheet_method_apply_cellarray (sheet.c:351)
==28476== by 0xD930DB7: largefile::CsvParser::run(void*) (CsvParser.cpp:147)
==28476== by 0xDD624C8: concurrent::thread_run(void*) (Thread.cpp:28)
==28476== 由 0xA7B73B9:start_thread(在 /lib/libpthread-2.9.so 中)
==28476== 由 0x80DBFCC:克隆(在 /lib/libc-2.9.so 中)
==28476== 地址 0xbc5d4a8 在访问指针的内部是 0 个字节
==28476== 一次合法范围,一个大小为 160 的块已释放
==28476== 在 0x4C25D4F:空闲 (vg_replace_malloc.c:323)
==28476== by 0xD9314CA: largefile::cb1(void*, unsigned long, void*) (CsvParser.cpp:57)
==28476== by 0xDB42681:csv_parse(在/home/jbellone/work/gtkworkbook/lib/libcs​​v.so)
==28476== by 0xD930D00: largefile::CsvParser::run(void*) (CsvParser.cpp:136)
==28476== by 0xDD624C8: concurrent::thread_run(void*) (Thread.cpp:28)
==28476== 由 0xA7B73B9:start_thread(在 /lib/libpthread-2.9.so 中)
==28476== 由 0x80DBFCC:克隆(在 /lib/libc-2.9.so 中)
==28476==
==28476== 大小为 8 的读取无效
==28476== 在 0x771AF66: sheet_method_apply_cellarray (sheet.c:351)
==28476== by 0xD930DB7: largefile::CsvParser::run(void*) (CsvParser.cpp:147)
==28476== by 0xDD624C8: concurrent::thread_run(void*) (Thread.cpp:28)
==28476== 由 0xA7B73B9:start_thread(在 /lib/libpthread-2.9.so 中)
==28476== 由 0x80DBFCC:克隆(在 /lib/libc-2.9.so 中)
==28476== 地址 0xbc5d4a8 在访问指针的内部是 0 个字节
==28476== 一次合法范围,一个大小为 160 的块已释放
==28476== 在 0x4C25D4F:空闲 (vg_replace_malloc.c:323)
==28476== by 0xD9314CA: largefile::cb1(void*, unsigned long, void*) (CsvParser.cpp:57)
==28476== by 0xDB42681:csv_parse(在/home/jbellone/work/gtkworkbook/lib/libcs​​v.so)
==28476== by 0xD930D00: largefile::CsvParser::run(void*) (CsvParser.cpp:136)
==28476== by 0xDD624C8: concurrent::thread_run(void*) (Thread.cpp:28)
==28476== 由 0xA7B73B9:start_thread(在 /lib/libpthread-2.9.so 中)
==28476== 由 0x80DBFCC:克隆(在 /lib/libc-2.9.so 中)

sheet.c 第 351 行


   gtk_sheet_set_cell_text (GTK_SHEET (sheet->gtk_sheet),
                 array[ii]->row,
                 array[ii]->column,
                 array[ii]->value->str);

sheet.c 中的整个函数:


static void
sheet_method_apply_cellarray (Sheet * sheet, 
                  Cell ** array,
                  gint size)
{
  ASSERT (sheet != NULL);
  g_return_if_fail (array != NULL);

  gdk_threads_enter ();

  /* We'll see how this performs for now. In the future we may want to go
     directly into the GtkSheet structures to get a little more performance
     boost (mainly because we should not have to check all the bounds each
     time we want to update). */
  for (gint ii = 0; ii gtk_sheet),
                 array[ii]->row,
                 array[ii]->column,
                 array[ii]->value->str);

    if (!IS_NULLSTR (array[ii]->attributes.bgcolor->str))
      sheet->range_set_background (sheet, 
                   &array[ii]->range, 
                   array[ii]->attributes.bgcolor->str);

    if (!IS_NULLSTR (array[ii]->attributes.fgcolor->str))
      sheet->range_set_foreground (sheet, 
                   &array[ii]->range, 
                   array[ii]->attributes.fgcolor->str);

    /* Clear all of the strings */
    g_string_assign (array[ii]->value, "");
    g_string_assign (array[ii]->attributes.bgcolor, "");
    g_string_assign (array[ii]->attributes.fgcolor, "");
  }

  gdk_threads_leave ();
}

CSV 解析器线程


  void *
  CsvParser::run (void * null) {
    this->running = true;
    std::queue queue;
    struct csv_parser csv;
    struct csv_column column = {this->wb->sheet_first,
                this->fields,
                this->maxOfFields,
                this->sizeOfFields,
                0,
                0,
                new char [1024]};

    if (csv_init (&csv, CSV_STRICT) != 0) {
      std::cerr running == true) {
      if (this->inputQueue.size() > 0) {

    // Lock, copy, clear, unlock. - Free this up.
    this->inputQueue.lock();
    this->inputQueue.copy (queue);
    this->inputQueue.clear();
    this->inputQueue.unlock();

    while (queue.size() > 0) {
      std::string buf = queue.front(); queue.pop();
      size_t bytes = buf.length();

      if (this->running == false)
        break;

      if ((bytes = csv_parse (&csv, buf.c_str(), bytes, cb1, cb2, &column)) == bytes) {
        if (csv_error (&csv) == CSV_EPARSE) {
          std::cerr wb->sheet_first->apply_array (this->wb->sheet_first,
                                          this->fields,
                                          this->sizeOfFields);

      if (column.row >= (column.sheet)->max_rows)
        column.row = 0;
    }
      } 
      concurrent::Thread::sleep(1);
    }

    return NULL;
  }
4

1 回答 1

2

我的猜测是你遇到了线程的竞争条件。当您的解析器尝试使用它时,Cb1 正在重新分配数组。在重新分配之后,旧地址不再有效,这就是您无效读取的来源。您需要在数组周围放置一个锁(可能是读取器/写入器锁)以避免遇到此问题。如果可能,请尝试单线程运行此代码以查看问题是否重现。如果不是,那就是线程问题,否则就完全是另外一回事了。

于 2009-06-04T21:18:12.467 回答