在libpango中调用pango_cairo_font_map_get_default()时,“ GLib-GObject-CRITICAL”的原因是什么?

This is a concept question regarding gtk/glib/libpango/libcairo. Let's straight to the problem.

I am wrapping an old C library in Go written by a former colleague, somewhere in the C code calls pango_cairo_font_map_get_default() to get a default font_map maintained by libpango.

The wrapping is basically from Go domain entering the C domain (foreign function interface) and C side using pthread creates a thread eventually calls pango_cairo_font_map_get_default.

Originally, everything works fine on the pure C side. After the wrapping, the C code stuck on the calling of pango_cairo_font_map_get_default()

printf("before call");
font_map = pango_cairo_font_map_get_default();
printf("after call");

The expected outputs are:

before call
after call

The actual outputs are:

before call
(process:1): GLib-GObject-WARNING **: cannot register existing type 'PangoFontMap'

(process:1): GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed

(process:1): GLib-GObject-CRITICAL **: g_type_register_static: assertion 'parent_type > 0' failed

(process:1): GLib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed

(process:1): GLib-GObject-CRITICAL **: g_type_register_static: assertion 'parent_type > 0' failed

(process:1): GLib-GObject-WARNING **: cannot register existing type 'PangoCairoFontMap'

My own simulation (The C side basically explains how my former colleague was doing with libpango) demo works fine, no warning and no critical outputs:

package main

/*
#cgo CFLAGS: -I/usr/local/include/pango-1.0
#cgo CFLAGS: -I/usr/local/include/glib-2.0
#cgo CFLAGS: -I/usr/local/include/cairo
#cgo LDFLAGS: -L/usr/local/lib -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0
#include <pango/pangocairo.h>
#include <stdio.h>
#include <pthread.h>
void hello() {
    printf("threa id: %li
", (unsigned long int)pthread_self());
    PangoFontMap* font_map = pango_cairo_font_map_get_default();
    PangoFontDescription* font_desc = pango_font_description_new();
    pango_font_description_set_family(font_desc, "monospace");
    pango_font_description_set_weight(font_desc, PANGO_WEIGHT_NORMAL);
    pango_font_description_set_size(font_desc, 20 * PANGO_SCALE * 700 / 96);
    PangoContext* context = pango_font_map_create_context(font_map);
    PangoFont* font = pango_font_map_load_font(font_map, context, font_desc);
    PangoFontMetrics* metrics = pango_font_get_metrics(font, NULL);
    int width = pango_font_metrics_get_approximate_digit_width(metrics) / PANGO_SCALE;
    int height = (pango_font_metrics_get_descent(metrics)
                    + pango_font_metrics_get_ascent(metrics)) / PANGO_SCALE;
    printf("%d, %d
", width, height);
    cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
    cairo_t* cairo = cairo_create(surface);
    cairo_set_source_rgb(cairo, 155 / 255.0, 155 / 255.0, 155 / 255.0);
    cairo_rectangle(cairo, 0, 0, width, height);
    PangoLayout* layout = pango_cairo_create_layout(cairo);
    pango_layout_set_font_description(layout, font_desc);
    g_object_unref(layout);
    cairo_destroy(cairo);
    cairo_surface_destroy(surface);
}
void* font() {
    hello();
    hello();
    return NULL;
}
void two_threads() {
    pthread_t thread1;
    pthread_create(&thread1, NULL, font, NULL);
    pthread_join(thread1, NULL);
    pthread_t thread2;
    pthread_create(&thread2, NULL, font, NULL);
    pthread_join(thread2, NULL);
}
*/
import "C"
import "sync"

func main() {
    wg := sync.WaitGroup{}
    for i := 0; i < 1; i++ {
        wg.Add(1)
        go func() {
            C.two_threads()
            wg.Done()
        }()
    }
    wg.Wait()
}

TL;DR: my understanding of pango_cairo_font_map_get_default() will always succeed (thread safe, because it is using 1.40.4 1.el7), and no matter how many times (static internal variable inside pango) the code tries to call.

Thus, my questions are:

  1. what can be a cause of cannot register existing type/g_once_init_leave: assertion failed?
  2. why such an error blocks the code?

The problem is likely due to a conflict between what you are doing in Go and c, due to doubly registering the type and being opaque to what each other are doing. Try to use the go bindings for the gtk stack rather than making your own wrapper.