Import Upstream version 2.72.4
This commit is contained in:
commit
4ef3ff9793
2003 changed files with 1332420 additions and 0 deletions
7
tests/assert-msg-test.c
Normal file
7
tests/assert-msg-test.c
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include <glib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_assert(42 < 0);
|
||||
return 0;
|
||||
}
|
||||
5
tests/assert-msg-test.gdb
Normal file
5
tests/assert-msg-test.gdb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
run
|
||||
set print elements 0
|
||||
# Work around https://sourceware.org/bugzilla/show_bug.cgi?id=22501
|
||||
print *((char**) &__glib_assert_msg)
|
||||
quit
|
||||
9
tests/collate/collate-1.file
Normal file
9
tests/collate/collate-1.file
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
223
|
||||
bar
|
||||
baz
|
||||
c
|
||||
eer34
|
||||
er1
|
||||
foo
|
||||
GTK+
|
||||
z
|
||||
9
tests/collate/collate-1.in
Normal file
9
tests/collate/collate-1.in
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
z
|
||||
c
|
||||
eer34
|
||||
223
|
||||
er1
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
GTK+
|
||||
9
tests/collate/collate-1.unicode
Normal file
9
tests/collate/collate-1.unicode
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
223
|
||||
bar
|
||||
baz
|
||||
c
|
||||
eer34
|
||||
er1
|
||||
foo
|
||||
GTK+
|
||||
z
|
||||
13
tests/collate/collate-2.file
Normal file
13
tests/collate/collate-2.file
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
bla001
|
||||
bla02
|
||||
bla03
|
||||
bla4
|
||||
bla10
|
||||
bla100
|
||||
event.c
|
||||
event.h
|
||||
eventgenerator.c
|
||||
file.c
|
||||
file.txt
|
||||
file2.bla
|
||||
file3.xx
|
||||
13
tests/collate/collate-2.in
Normal file
13
tests/collate/collate-2.in
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
file.txt
|
||||
file2.bla
|
||||
file.c
|
||||
file3.xx
|
||||
bla001
|
||||
bla02
|
||||
bla03
|
||||
bla4
|
||||
bla10
|
||||
bla100
|
||||
event.c
|
||||
eventgenerator.c
|
||||
event.h
|
||||
13
tests/collate/collate-2.unicode
Normal file
13
tests/collate/collate-2.unicode
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
bla001
|
||||
bla02
|
||||
bla03
|
||||
bla10
|
||||
bla100
|
||||
bla4
|
||||
event.c
|
||||
eventgenerator.c
|
||||
event.h
|
||||
file2.bla
|
||||
file3.xx
|
||||
file.c
|
||||
file.txt
|
||||
421
tests/gio-test.c
Normal file
421
tests/gio-test.c
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 2000 Tor Lillqvist
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* A test program for the main loop and IO channel code.
|
||||
* Just run it. Optional parameter is number of sub-processes.
|
||||
*/
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <process.h>
|
||||
#define STRICT
|
||||
#include <windows.h>
|
||||
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
|
||||
#endif
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static int nrunning;
|
||||
static GMainLoop *main_loop;
|
||||
|
||||
#define BUFSIZE 5000 /* Larger than the circular buffer in
|
||||
* giowin32.c on purpose.
|
||||
*/
|
||||
|
||||
static int nkiddies;
|
||||
|
||||
static struct {
|
||||
int fd;
|
||||
int seq;
|
||||
} *seqtab;
|
||||
|
||||
static GIOError
|
||||
read_all (int fd,
|
||||
GIOChannel *channel,
|
||||
char *buffer,
|
||||
guint nbytes,
|
||||
guint *bytes_read)
|
||||
{
|
||||
guint left = nbytes;
|
||||
gsize nb;
|
||||
GIOError error = G_IO_ERROR_NONE;
|
||||
char *bufp = buffer;
|
||||
|
||||
/* g_io_channel_read() doesn't necessarily return all the
|
||||
* data we want at once.
|
||||
*/
|
||||
*bytes_read = 0;
|
||||
while (left)
|
||||
{
|
||||
error = g_io_channel_read (channel, bufp, left, &nb);
|
||||
|
||||
if (error != G_IO_ERROR_NONE)
|
||||
{
|
||||
g_print ("gio-test: ...from %d: %d\n", fd, error);
|
||||
if (error == G_IO_ERROR_AGAIN)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (nb == 0)
|
||||
return error;
|
||||
left -= nb;
|
||||
bufp += nb;
|
||||
*bytes_read += nb;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_source (gpointer data)
|
||||
{
|
||||
if (g_source_remove (*(guint *) data))
|
||||
{
|
||||
nrunning--;
|
||||
if (nrunning == 0)
|
||||
g_main_loop_quit (main_loop);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
recv_message (GIOChannel *channel,
|
||||
GIOCondition cond,
|
||||
gpointer data)
|
||||
{
|
||||
gint fd = g_io_channel_unix_get_fd (channel);
|
||||
gboolean retval = TRUE;
|
||||
|
||||
g_debug ("gio-test: ...from %d:%s%s%s%s", fd,
|
||||
(cond & G_IO_ERR) ? " ERR" : "",
|
||||
(cond & G_IO_HUP) ? " HUP" : "",
|
||||
(cond & G_IO_IN) ? " IN" : "",
|
||||
(cond & G_IO_PRI) ? " PRI" : "");
|
||||
|
||||
if (cond & (G_IO_ERR | G_IO_HUP))
|
||||
{
|
||||
shutdown_source (data);
|
||||
retval = FALSE;
|
||||
}
|
||||
|
||||
if (cond & G_IO_IN)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
guint nbytes = 0;
|
||||
guint nb;
|
||||
guint j;
|
||||
int i, seq;
|
||||
GIOError error;
|
||||
|
||||
error = read_all (fd, channel, (gchar *) &seq, sizeof (seq), &nb);
|
||||
if (error == G_IO_ERROR_NONE)
|
||||
{
|
||||
if (nb == 0)
|
||||
{
|
||||
g_debug ("gio-test: ...from %d: EOF", fd);
|
||||
shutdown_source (data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert (nb == sizeof (nbytes));
|
||||
|
||||
for (i = 0; i < nkiddies; i++)
|
||||
if (seqtab[i].fd == fd)
|
||||
{
|
||||
g_assert_cmpint (seq, ==, seqtab[i].seq);
|
||||
seqtab[i].seq++;
|
||||
break;
|
||||
}
|
||||
|
||||
error = read_all (fd, channel, (gchar *) &nbytes, sizeof (nbytes), &nb);
|
||||
}
|
||||
|
||||
if (error != G_IO_ERROR_NONE)
|
||||
return FALSE;
|
||||
|
||||
if (nb == 0)
|
||||
{
|
||||
g_debug ("gio-test: ...from %d: EOF", fd);
|
||||
shutdown_source (data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert (nb == sizeof (nbytes));
|
||||
|
||||
g_assert_cmpint (nbytes, <, BUFSIZE);
|
||||
g_assert (nbytes < BUFSIZE);
|
||||
g_debug ("gio-test: ...from %d: %d bytes", fd, nbytes);
|
||||
if (nbytes > 0)
|
||||
{
|
||||
error = read_all (fd, channel, buf, nbytes, &nb);
|
||||
|
||||
if (error != G_IO_ERROR_NONE)
|
||||
return FALSE;
|
||||
|
||||
if (nb == 0)
|
||||
{
|
||||
g_debug ("gio-test: ...from %d: EOF", fd);
|
||||
shutdown_source (data);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (j = 0; j < nbytes; j++)
|
||||
g_assert (buf[j] == ' ' + (char) ((nbytes + j) % 95));
|
||||
g_debug ("gio-test: ...from %d: OK", fd);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
|
||||
static gboolean
|
||||
recv_windows_message (GIOChannel *channel,
|
||||
GIOCondition cond,
|
||||
gpointer data)
|
||||
{
|
||||
GIOError error;
|
||||
MSG msg;
|
||||
gsize nb;
|
||||
|
||||
while (1)
|
||||
{
|
||||
error = g_io_channel_read (channel, (gchar *) &msg, sizeof (MSG), &nb);
|
||||
|
||||
if (error != G_IO_ERROR_NONE)
|
||||
{
|
||||
g_print ("gio-test: ...reading Windows message: G_IO_ERROR_%s\n",
|
||||
(error == G_IO_ERROR_AGAIN ? "AGAIN" :
|
||||
(error == G_IO_ERROR_INVAL ? "INVAL" :
|
||||
(error == G_IO_ERROR_UNKNOWN ? "UNKNOWN" : "???"))));
|
||||
if (error == G_IO_ERROR_AGAIN)
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
g_print ("gio-test: ...Windows message for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
|
||||
msg.hwnd, msg.message, msg.wParam, (gintptr)msg.lParam);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK window_procedure (HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
|
||||
LRESULT CALLBACK
|
||||
window_procedure (HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
g_print ("gio-test: window_procedure for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
|
||||
hwnd, message, wparam, (gintptr)lparam);
|
||||
return DefWindowProc (hwnd, message, wparam, lparam);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
/* Parent */
|
||||
|
||||
GIOChannel *my_read_channel;
|
||||
gchar *cmdline;
|
||||
int i;
|
||||
#ifdef G_OS_WIN32
|
||||
GTimeVal start, end;
|
||||
GPollFD pollfd;
|
||||
int pollresult;
|
||||
ATOM klass;
|
||||
static WNDCLASS wcl;
|
||||
HWND hwnd;
|
||||
GIOChannel *windows_messages_channel;
|
||||
#endif
|
||||
|
||||
nkiddies = (argc == 1 ? 1 : atoi(argv[1]));
|
||||
seqtab = g_malloc (nkiddies * 2 * sizeof (int));
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
wcl.style = 0;
|
||||
wcl.lpfnWndProc = window_procedure;
|
||||
wcl.cbClsExtra = 0;
|
||||
wcl.cbWndExtra = 0;
|
||||
wcl.hInstance = GetModuleHandle (NULL);
|
||||
wcl.hIcon = NULL;
|
||||
wcl.hCursor = NULL;
|
||||
wcl.hbrBackground = NULL;
|
||||
wcl.lpszMenuName = NULL;
|
||||
wcl.lpszClassName = "gio-test";
|
||||
|
||||
klass = RegisterClass (&wcl);
|
||||
|
||||
if (!klass)
|
||||
{
|
||||
g_print ("gio-test: RegisterClass failed\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
hwnd = CreateWindow (MAKEINTATOM(klass), "gio-test", 0, 0, 0, 10, 10,
|
||||
NULL, NULL, wcl.hInstance, NULL);
|
||||
if (!hwnd)
|
||||
{
|
||||
g_print ("gio-test: CreateWindow failed\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
windows_messages_channel = g_io_channel_win32_new_messages ((guint) (guintptr) hwnd);
|
||||
g_io_add_watch (windows_messages_channel, G_IO_IN, recv_windows_message, 0);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < nkiddies; i++)
|
||||
{
|
||||
int pipe_to_sub[2], pipe_from_sub[2];
|
||||
guint *id;
|
||||
|
||||
if (pipe (pipe_to_sub) == -1 ||
|
||||
pipe (pipe_from_sub) == -1)
|
||||
perror ("pipe"), exit (1);
|
||||
|
||||
seqtab[i].fd = pipe_from_sub[0];
|
||||
seqtab[i].seq = 0;
|
||||
|
||||
my_read_channel = g_io_channel_unix_new (pipe_from_sub[0]);
|
||||
|
||||
id = g_new (guint, 1);
|
||||
*id =
|
||||
g_io_add_watch_full (my_read_channel,
|
||||
G_PRIORITY_DEFAULT,
|
||||
G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
|
||||
recv_message,
|
||||
id, g_free);
|
||||
|
||||
nrunning++;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
cmdline = g_strdup_printf ("%d:%d:0x%p",
|
||||
pipe_to_sub[0],
|
||||
pipe_from_sub[1],
|
||||
hwnd);
|
||||
_spawnl (_P_NOWAIT, argv[0], argv[0], "--child", cmdline, NULL);
|
||||
#else
|
||||
cmdline = g_strdup_printf ("%s --child %d:%d &", argv[0],
|
||||
pipe_to_sub[0], pipe_from_sub[1]);
|
||||
|
||||
system (cmdline);
|
||||
g_free (cmdline);
|
||||
#endif
|
||||
close (pipe_to_sub[0]);
|
||||
close (pipe_from_sub [1]);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
g_get_current_time (&start);
|
||||
g_io_channel_win32_make_pollfd (my_read_channel, G_IO_IN, &pollfd);
|
||||
pollresult = g_io_channel_win32_poll (&pollfd, 1, 100);
|
||||
g_get_current_time (&end);
|
||||
if (end.tv_usec < start.tv_usec)
|
||||
end.tv_sec--, end.tv_usec += 1000000;
|
||||
g_print ("gio-test: had to wait %ld.%03ld s, result:%d\n",
|
||||
end.tv_sec - start.tv_sec,
|
||||
(end.tv_usec - start.tv_usec) / 1000,
|
||||
pollresult);
|
||||
#endif
|
||||
g_io_channel_unref (my_read_channel);
|
||||
}
|
||||
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
g_main_loop_run (main_loop);
|
||||
|
||||
g_main_loop_unref (main_loop);
|
||||
g_free (seqtab);
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
/* Child */
|
||||
|
||||
int readfd, writefd;
|
||||
#ifdef G_OS_WIN32
|
||||
HWND hwnd;
|
||||
#endif
|
||||
int i, j;
|
||||
char buf[BUFSIZE];
|
||||
int buflen;
|
||||
GTimeVal tv;
|
||||
int n;
|
||||
|
||||
g_get_current_time (&tv);
|
||||
|
||||
sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
sscanf (argv[2] + n, ":0x%p", &hwnd);
|
||||
#endif
|
||||
|
||||
srand (tv.tv_sec ^ (tv.tv_usec / 1000) ^ readfd ^ (writefd << 4));
|
||||
|
||||
for (i = 0; i < 20 + rand() % 20; i++)
|
||||
{
|
||||
g_usleep (100 + (rand() % 10) * 5000);
|
||||
buflen = rand() % BUFSIZE;
|
||||
for (j = 0; j < buflen; j++)
|
||||
buf[j] = ' ' + ((buflen + j) % 95);
|
||||
g_debug ("gio-test: child writing %d+%d bytes to %d",
|
||||
(int)(sizeof(i) + sizeof(buflen)), buflen, writefd);
|
||||
write (writefd, &i, sizeof (i));
|
||||
write (writefd, &buflen, sizeof (buflen));
|
||||
write (writefd, buf, buflen);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
if (rand() % 100 < 5)
|
||||
{
|
||||
int msg = WM_USER + (rand() % 100);
|
||||
WPARAM wparam = rand ();
|
||||
LPARAM lparam = rand ();
|
||||
g_print ("gio-test: child posting message %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT " to 0x%p\n",
|
||||
msg, wparam, (gintptr)lparam, hwnd);
|
||||
PostMessage (hwnd, msg, wparam, lparam);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
g_debug ("gio-test: child exiting, closing %d", writefd);
|
||||
close (writefd);
|
||||
}
|
||||
else
|
||||
g_print ("Huh?\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
tests/gobject/.gitignore
vendored
Normal file
15
tests/gobject/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
accumulator
|
||||
defaultiface
|
||||
dynamictype
|
||||
gvalue-test
|
||||
ifacecheck
|
||||
ifaceinherit
|
||||
ifaceinit
|
||||
ifaceproperties
|
||||
override
|
||||
paramspec-test
|
||||
performance
|
||||
performance-threaded
|
||||
references
|
||||
signals
|
||||
singleton
|
||||
307
tests/gobject/accumulator.c
Normal file
307
tests/gobject/accumulator.c
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2001, 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestAccumulator"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "testmarshal.h"
|
||||
#include "testcommon.h"
|
||||
|
||||
/* What this test tests is the behavior of signal accumulators
|
||||
* Two accumulators are tested:
|
||||
*
|
||||
* 1: A custom accumulator that appends the returned strings
|
||||
* 2: The standard g_signal_accumulator_true_handled that stops
|
||||
* emission on TRUE returns.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TestObject, a parent class for TestObject
|
||||
*/
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
typedef struct _TestObject TestObject;
|
||||
typedef struct _TestObjectClass TestObjectClass;
|
||||
|
||||
struct _TestObject
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
struct _TestObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gchar* (*test_signal1) (TestObject *tobject,
|
||||
gint param);
|
||||
gboolean (*test_signal2) (TestObject *tobject,
|
||||
gint param);
|
||||
GVariant* (*test_signal3) (TestObject *tobject,
|
||||
gboolean *weak_ptr);
|
||||
};
|
||||
|
||||
static GType test_object_get_type (void);
|
||||
|
||||
static gboolean
|
||||
test_signal1_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer data)
|
||||
{
|
||||
const gchar *accu_string = g_value_get_string (return_accu);
|
||||
const gchar *new_string = g_value_get_string (handler_return);
|
||||
gchar *result_string;
|
||||
|
||||
if (accu_string)
|
||||
result_string = g_strconcat (accu_string, new_string, NULL);
|
||||
else if (new_string)
|
||||
result_string = g_strdup (new_string);
|
||||
else
|
||||
result_string = NULL;
|
||||
|
||||
g_value_set_string_take_ownership (return_accu, result_string);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_object_signal1_callback_before (TestObject *tobject,
|
||||
gint param,
|
||||
gpointer data)
|
||||
{
|
||||
return g_strdup ("<before>");
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_object_real_signal1 (TestObject *tobject,
|
||||
gint param)
|
||||
{
|
||||
return g_strdup ("<default>");
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_object_signal1_callback_after (TestObject *tobject,
|
||||
gint param,
|
||||
gpointer data)
|
||||
{
|
||||
return g_strdup ("<after>");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_object_signal2_callback_before (TestObject *tobject,
|
||||
gint param)
|
||||
{
|
||||
switch (param)
|
||||
{
|
||||
case 1: return TRUE;
|
||||
case 2: return FALSE;
|
||||
case 3: return FALSE;
|
||||
case 4: return FALSE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_object_real_signal2 (TestObject *tobject,
|
||||
gint param)
|
||||
{
|
||||
switch (param)
|
||||
{
|
||||
case 1: g_assert_not_reached (); return FALSE;
|
||||
case 2: return TRUE;
|
||||
case 3: return FALSE;
|
||||
case 4: return FALSE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_object_signal2_callback_after (TestObject *tobject,
|
||||
gint param)
|
||||
{
|
||||
switch (param)
|
||||
{
|
||||
case 1: g_assert_not_reached (); return FALSE;
|
||||
case 2: g_assert_not_reached (); return FALSE;
|
||||
case 3: return TRUE;
|
||||
case 4: return FALSE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_signal3_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer data)
|
||||
{
|
||||
GVariant *variant;
|
||||
|
||||
variant = g_value_get_variant (handler_return);
|
||||
g_assert (!g_variant_is_floating (variant));
|
||||
|
||||
g_value_set_variant (return_accu, variant);
|
||||
|
||||
return variant == NULL;
|
||||
}
|
||||
|
||||
/* To be notified when the variant is finalised, we construct
|
||||
* it from data with a custom GDestroyNotify.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
char *mem;
|
||||
gsize n;
|
||||
gboolean *weak_ptr;
|
||||
} VariantData;
|
||||
|
||||
static void
|
||||
free_data (VariantData *data)
|
||||
{
|
||||
*(data->weak_ptr) = TRUE;
|
||||
g_free (data->mem);
|
||||
g_slice_free (VariantData, data);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
test_object_real_signal3 (TestObject *tobject,
|
||||
gboolean *weak_ptr)
|
||||
{
|
||||
GVariant *variant;
|
||||
VariantData *data;
|
||||
|
||||
variant = g_variant_ref_sink (g_variant_new_uint32 (42));
|
||||
data = g_slice_new (VariantData);
|
||||
data->weak_ptr = weak_ptr;
|
||||
data->n = g_variant_get_size (variant);
|
||||
data->mem = g_malloc (data->n);
|
||||
g_variant_store (variant, data->mem);
|
||||
g_variant_unref (variant);
|
||||
|
||||
variant = g_variant_new_from_data (G_VARIANT_TYPE ("u"),
|
||||
data->mem,
|
||||
data->n,
|
||||
TRUE,
|
||||
(GDestroyNotify) free_data,
|
||||
data);
|
||||
return g_variant_ref_sink (variant);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_class_init (TestObjectClass *class)
|
||||
{
|
||||
class->test_signal1 = test_object_real_signal1;
|
||||
class->test_signal2 = test_object_real_signal2;
|
||||
class->test_signal3 = test_object_real_signal3;
|
||||
|
||||
g_signal_new ("test-signal1",
|
||||
G_OBJECT_CLASS_TYPE (class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestObjectClass, test_signal1),
|
||||
test_signal1_accumulator, NULL,
|
||||
test_marshal_STRING__INT,
|
||||
G_TYPE_STRING, 1, G_TYPE_INT);
|
||||
g_signal_new ("test-signal2",
|
||||
G_OBJECT_CLASS_TYPE (class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestObjectClass, test_signal2),
|
||||
g_signal_accumulator_true_handled, NULL,
|
||||
test_marshal_BOOLEAN__INT,
|
||||
G_TYPE_BOOLEAN, 1, G_TYPE_INT);
|
||||
g_signal_new ("test-signal3",
|
||||
G_OBJECT_CLASS_TYPE (class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestObjectClass, test_signal3),
|
||||
test_signal3_accumulator, NULL,
|
||||
test_marshal_VARIANT__POINTER,
|
||||
G_TYPE_VARIANT, 1, G_TYPE_POINTER);
|
||||
}
|
||||
|
||||
static DEFINE_TYPE(TestObject, test_object,
|
||||
test_object_class_init, NULL, NULL,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
TestObject *object;
|
||||
gchar *string_result;
|
||||
gboolean bool_result;
|
||||
gboolean variant_finalised;
|
||||
GVariant *variant_result;
|
||||
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_signal_connect (object, "test-signal1",
|
||||
G_CALLBACK (test_object_signal1_callback_before), NULL);
|
||||
g_signal_connect_after (object, "test-signal1",
|
||||
G_CALLBACK (test_object_signal1_callback_after), NULL);
|
||||
|
||||
g_signal_emit_by_name (object, "test-signal1", 0, &string_result);
|
||||
g_assert (strcmp (string_result, "<before><default><after>") == 0);
|
||||
g_free (string_result);
|
||||
|
||||
g_signal_connect (object, "test-signal2",
|
||||
G_CALLBACK (test_object_signal2_callback_before), NULL);
|
||||
g_signal_connect_after (object, "test-signal2",
|
||||
G_CALLBACK (test_object_signal2_callback_after), NULL);
|
||||
|
||||
bool_result = FALSE;
|
||||
g_signal_emit_by_name (object, "test-signal2", 1, &bool_result);
|
||||
g_assert (bool_result == TRUE);
|
||||
bool_result = FALSE;
|
||||
g_signal_emit_by_name (object, "test-signal2", 2, &bool_result);
|
||||
g_assert (bool_result == TRUE);
|
||||
bool_result = FALSE;
|
||||
g_signal_emit_by_name (object, "test-signal2", 3, &bool_result);
|
||||
g_assert (bool_result == TRUE);
|
||||
bool_result = TRUE;
|
||||
g_signal_emit_by_name (object, "test-signal2", 4, &bool_result);
|
||||
g_assert (bool_result == FALSE);
|
||||
|
||||
variant_finalised = FALSE;
|
||||
variant_result = NULL;
|
||||
g_signal_emit_by_name (object, "test-signal3", &variant_finalised, &variant_result);
|
||||
g_assert (variant_result != NULL);
|
||||
g_assert (!g_variant_is_floating (variant_result));
|
||||
|
||||
/* Test that variant_result had refcount 1 */
|
||||
g_assert (!variant_finalised);
|
||||
g_variant_unref (variant_result);
|
||||
g_assert (variant_finalised);
|
||||
|
||||
g_object_unref (object);
|
||||
|
||||
return 0;
|
||||
}
|
||||
199
tests/gobject/defaultiface.c
Normal file
199
tests/gobject/defaultiface.c
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2001, 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestDefaultIface"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "testcommon.h"
|
||||
#include "testmodule.h"
|
||||
|
||||
/* This test tests getting the default vtable for an interface
|
||||
* and the initialization and finalization of such default
|
||||
* interfaces.
|
||||
*
|
||||
* We test this both for static and for dynamic interfaces.
|
||||
*/
|
||||
|
||||
/**********************************************************************
|
||||
* Static interface tests
|
||||
**********************************************************************/
|
||||
|
||||
typedef struct _TestStaticIfaceClass TestStaticIfaceClass;
|
||||
|
||||
struct _TestStaticIfaceClass
|
||||
{
|
||||
GTypeInterface base_iface;
|
||||
guint val;
|
||||
};
|
||||
|
||||
GType test_static_iface_get_type (void);
|
||||
#define TEST_TYPE_STATIC_IFACE (test_static_iface_get_type ())
|
||||
|
||||
static void
|
||||
test_static_iface_default_init (TestStaticIfaceClass *iface)
|
||||
{
|
||||
iface->val = 42;
|
||||
}
|
||||
|
||||
DEFINE_IFACE (TestStaticIface, test_static_iface,
|
||||
NULL, test_static_iface_default_init)
|
||||
|
||||
static void
|
||||
test_static_iface (void)
|
||||
{
|
||||
TestStaticIfaceClass *static_iface;
|
||||
|
||||
/* Not loaded until we call ref for the first time */
|
||||
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
|
||||
g_assert (static_iface == NULL);
|
||||
|
||||
/* Ref loads */
|
||||
static_iface = g_type_default_interface_ref (TEST_TYPE_STATIC_IFACE);
|
||||
g_assert (static_iface && static_iface->val == 42);
|
||||
|
||||
/* Peek then works */
|
||||
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
|
||||
g_assert (static_iface && static_iface->val == 42);
|
||||
|
||||
/* Unref does nothing */
|
||||
g_type_default_interface_unref (static_iface);
|
||||
|
||||
/* And peek still works */
|
||||
static_iface = g_type_default_interface_peek (TEST_TYPE_STATIC_IFACE);
|
||||
g_assert (static_iface && static_iface->val == 42);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Dynamic interface tests
|
||||
**********************************************************************/
|
||||
|
||||
typedef struct _TestDynamicIfaceClass TestDynamicIfaceClass;
|
||||
|
||||
struct _TestDynamicIfaceClass
|
||||
{
|
||||
GTypeInterface base_iface;
|
||||
guint val;
|
||||
};
|
||||
|
||||
static GType test_dynamic_iface_type;
|
||||
static gboolean dynamic_iface_init = FALSE;
|
||||
|
||||
#define TEST_TYPE_DYNAMIC_IFACE (test_dynamic_iface_type)
|
||||
|
||||
static void
|
||||
test_dynamic_iface_default_init (TestStaticIfaceClass *iface)
|
||||
{
|
||||
dynamic_iface_init = TRUE;
|
||||
iface->val = 42;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dynamic_iface_default_finalize (TestStaticIfaceClass *iface)
|
||||
{
|
||||
dynamic_iface_init = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dynamic_iface_register (GTypeModule *module)
|
||||
{
|
||||
const GTypeInfo iface_info =
|
||||
{
|
||||
sizeof (TestDynamicIfaceClass),
|
||||
(GBaseInitFunc) NULL,
|
||||
(GBaseFinalizeFunc) NULL,
|
||||
(GClassInitFunc) test_dynamic_iface_default_init,
|
||||
(GClassFinalizeFunc) test_dynamic_iface_default_finalize,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_dynamic_iface_type = g_type_module_register_type (module, G_TYPE_INTERFACE,
|
||||
"TestDynamicIface", &iface_info, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
module_register (GTypeModule *module)
|
||||
{
|
||||
test_dynamic_iface_register (module);
|
||||
}
|
||||
|
||||
static void
|
||||
test_dynamic_iface (void)
|
||||
{
|
||||
TestDynamicIfaceClass *dynamic_iface;
|
||||
|
||||
test_module_new (module_register);
|
||||
|
||||
/* Not loaded until we call ref for the first time */
|
||||
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
|
||||
g_assert (dynamic_iface == NULL);
|
||||
|
||||
/* Ref loads */
|
||||
dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
|
||||
g_assert (dynamic_iface_init);
|
||||
g_assert (dynamic_iface && dynamic_iface->val == 42);
|
||||
|
||||
/* Peek then works */
|
||||
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
|
||||
g_assert (dynamic_iface && dynamic_iface->val == 42);
|
||||
|
||||
/* Unref causes finalize */
|
||||
g_type_default_interface_unref (dynamic_iface);
|
||||
#if 0
|
||||
g_assert (!dynamic_iface_init);
|
||||
#endif
|
||||
|
||||
/* Peek returns NULL */
|
||||
dynamic_iface = g_type_default_interface_peek (TEST_TYPE_DYNAMIC_IFACE);
|
||||
#if 0
|
||||
g_assert (dynamic_iface == NULL);
|
||||
#endif
|
||||
|
||||
/* Ref reloads */
|
||||
dynamic_iface = g_type_default_interface_ref (TEST_TYPE_DYNAMIC_IFACE);
|
||||
g_assert (dynamic_iface_init);
|
||||
g_assert (dynamic_iface && dynamic_iface->val == 42);
|
||||
|
||||
/* And Unref causes finalize once more*/
|
||||
g_type_default_interface_unref (dynamic_iface);
|
||||
#if 0
|
||||
g_assert (!dynamic_iface_init);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
test_static_iface ();
|
||||
test_dynamic_iface ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
59
tests/gobject/deftype.c
Normal file
59
tests/gobject/deftype.c
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* deftype.c
|
||||
* Copyright (C) 2006 Behdad Esfahbod
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <glib-object.h>
|
||||
|
||||
/* see http://bugzilla.gnome.org/show_bug.cgi?id=337128 for the purpose of this test */
|
||||
|
||||
#define MY_G_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init) { \
|
||||
const GInterfaceInfo g_implement_interface_info = { \
|
||||
(GInterfaceInitFunc) iface_init, \
|
||||
NULL, \
|
||||
NULL \
|
||||
}; \
|
||||
g_type_add_interface_static (g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
|
||||
}
|
||||
|
||||
#define MY_DEFINE_TYPE(TN, t_n, T_P) \
|
||||
G_DEFINE_TYPE_WITH_CODE (TN, t_n, T_P, \
|
||||
MY_G_IMPLEMENT_INTERFACE (G_TYPE_INTERFACE, NULL))
|
||||
|
||||
typedef struct _TypeName {
|
||||
GObject parent_instance;
|
||||
const char *name;
|
||||
} TypeName;
|
||||
|
||||
typedef struct _TypeNameClass {
|
||||
GObjectClass parent_parent;
|
||||
} TypeNameClass;
|
||||
|
||||
GType type_name_get_type (void);
|
||||
|
||||
MY_DEFINE_TYPE (TypeName, type_name, G_TYPE_OBJECT)
|
||||
|
||||
static void type_name_init (TypeName *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void type_name_class_init (TypeNameClass *klass)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
175
tests/gobject/dynamictype.c
Normal file
175
tests/gobject/dynamictype.c
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2001, 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestDynamicType"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "testcommon.h"
|
||||
#include "testmodule.h"
|
||||
|
||||
/* This test tests the macros for defining dynamic types.
|
||||
*/
|
||||
|
||||
static gboolean loaded = FALSE;
|
||||
|
||||
struct _TestIfaceClass
|
||||
{
|
||||
GTypeInterface base_iface;
|
||||
guint val;
|
||||
};
|
||||
|
||||
static GType test_iface_get_type (void);
|
||||
#define TEST_TYPE_IFACE (test_iface_get_type ())
|
||||
#define TEST_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE, TestIfaceClass))
|
||||
typedef struct _TestIface TestIface;
|
||||
typedef struct _TestIfaceClass TestIfaceClass;
|
||||
|
||||
static void test_iface_base_init (TestIfaceClass *iface);
|
||||
static void test_iface_default_init (TestIfaceClass *iface, gpointer class_data);
|
||||
|
||||
static DEFINE_IFACE(TestIface, test_iface, test_iface_base_init, test_iface_default_init)
|
||||
|
||||
static void
|
||||
test_iface_default_init (TestIfaceClass *iface,
|
||||
gpointer class_data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
test_iface_base_init (TestIfaceClass *iface)
|
||||
{
|
||||
}
|
||||
|
||||
GType dynamic_object_get_type (void);
|
||||
#define DYNAMIC_OBJECT_TYPE (dynamic_object_get_type ())
|
||||
|
||||
typedef GObject DynamicObject;
|
||||
typedef struct _DynamicObjectClass DynamicObjectClass;
|
||||
|
||||
struct _DynamicObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
guint val;
|
||||
};
|
||||
|
||||
static void dynamic_object_iface_init (TestIface *iface);
|
||||
|
||||
G_DEFINE_DYNAMIC_TYPE_EXTENDED(DynamicObject, dynamic_object, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE_DYNAMIC (TEST_TYPE_IFACE,
|
||||
dynamic_object_iface_init));
|
||||
|
||||
static void
|
||||
dynamic_object_class_init (DynamicObjectClass *class)
|
||||
{
|
||||
class->val = 42;
|
||||
loaded = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
dynamic_object_class_finalize (DynamicObjectClass *class)
|
||||
{
|
||||
loaded = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
dynamic_object_iface_init (TestIface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
dynamic_object_init (DynamicObject *dynamic_object)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
module_register (GTypeModule *module)
|
||||
{
|
||||
dynamic_object_register_type (module);
|
||||
}
|
||||
|
||||
static void
|
||||
test_dynamic_type (void)
|
||||
{
|
||||
DynamicObjectClass *class;
|
||||
|
||||
test_module_new (module_register);
|
||||
|
||||
/* Not loaded until we call ref for the first time */
|
||||
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
|
||||
g_assert (class == NULL);
|
||||
g_assert (!loaded);
|
||||
|
||||
/* Make sure interfaces work */
|
||||
g_assert (g_type_is_a (DYNAMIC_OBJECT_TYPE,
|
||||
TEST_TYPE_IFACE));
|
||||
|
||||
/* Ref loads */
|
||||
class = g_type_class_ref (DYNAMIC_OBJECT_TYPE);
|
||||
g_assert (class && class->val == 42);
|
||||
g_assert (loaded);
|
||||
|
||||
/* Peek then works */
|
||||
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
|
||||
g_assert (class && class->val == 42);
|
||||
g_assert (loaded);
|
||||
|
||||
/* Make sure interfaces still work */
|
||||
g_assert (g_type_is_a (DYNAMIC_OBJECT_TYPE,
|
||||
TEST_TYPE_IFACE));
|
||||
|
||||
/* Unref causes finalize */
|
||||
g_type_class_unref (class);
|
||||
|
||||
/* Peek returns NULL */
|
||||
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
|
||||
#if 0
|
||||
g_assert (!class);
|
||||
g_assert (!loaded);
|
||||
#endif
|
||||
|
||||
/* Ref reloads */
|
||||
class = g_type_class_ref (DYNAMIC_OBJECT_TYPE);
|
||||
g_assert (class && class->val == 42);
|
||||
g_assert (loaded);
|
||||
|
||||
/* And Unref causes finalize once more*/
|
||||
g_type_class_unref (class);
|
||||
class = g_type_class_peek (DYNAMIC_OBJECT_TYPE);
|
||||
#if 0
|
||||
g_assert (!class);
|
||||
g_assert (!loaded);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
test_dynamic_type ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
98
tests/gobject/meson.build
Normal file
98
tests/gobject/meson.build
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# We cannot use gnome.genmarshal() here
|
||||
testmarshal_h = custom_target('testmarshal_h',
|
||||
output : 'testmarshal.h',
|
||||
input : 'testmarshal.list',
|
||||
command : [
|
||||
python, glib_genmarshal,
|
||||
'--prefix=test_marshal',
|
||||
'--output=@OUTPUT@',
|
||||
'--quiet',
|
||||
'--header',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
testmarshal_c = custom_target('testmarshal_c',
|
||||
output : 'testmarshal.c',
|
||||
input : 'testmarshal.list',
|
||||
command : [
|
||||
python, glib_genmarshal,
|
||||
'--prefix=test_marshal',
|
||||
'--include-header=testmarshal.h',
|
||||
'--output=@OUTPUT@',
|
||||
'--quiet',
|
||||
'--body',
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
gobject_tests = {
|
||||
'deftype' : {},
|
||||
'defaultiface' : {
|
||||
'extra_sources' : ['testmodule.c'],
|
||||
},
|
||||
'dynamictype' : {
|
||||
'extra_sources' : ['testmodule.c'],
|
||||
},
|
||||
'override' : {},
|
||||
'signals' : {},
|
||||
'singleton' : {},
|
||||
'references' : {},
|
||||
'testgobject' : {},
|
||||
'accumulator' : {
|
||||
'extra_sources' : [testmarshal_c, testmarshal_h],
|
||||
},
|
||||
}
|
||||
|
||||
if host_system != 'windows'
|
||||
gobject_tests += {
|
||||
'timeloop-closure' : {},
|
||||
}
|
||||
endif
|
||||
|
||||
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
|
||||
common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
|
||||
|
||||
foreach test_name, extra_args : gobject_tests
|
||||
source = extra_args.get('source', test_name + '.c')
|
||||
extra_sources = extra_args.get('extra_sources', [])
|
||||
install = installed_tests_enabled and extra_args.get('install', true)
|
||||
template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
|
||||
|
||||
if install
|
||||
test_conf = configuration_data()
|
||||
test_conf.set('installed_tests_dir', installed_tests_execdir)
|
||||
test_conf.set('program', test_name)
|
||||
test_conf.set('env', '')
|
||||
configure_file(
|
||||
input: template,
|
||||
output: test_name + '.test',
|
||||
install_dir: installed_tests_metadir,
|
||||
configuration: test_conf
|
||||
)
|
||||
endif
|
||||
|
||||
# FIXME? $(GLIB_DEBUG_FLAGS)
|
||||
exe = executable(test_name, [source, extra_sources],
|
||||
c_args : common_c_args + extra_args.get('c_args', []),
|
||||
dependencies : common_deps + extra_args.get('dependencies', []),
|
||||
install_dir: installed_tests_execdir,
|
||||
install: install,
|
||||
)
|
||||
|
||||
suite = ['gobject'] + extra_args.get('suite', [])
|
||||
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
|
||||
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
|
||||
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
|
||||
endforeach
|
||||
|
||||
# Don't install these ones, and keep them out of 'make check' because they take too long...
|
||||
executable('performance', 'performance.c',
|
||||
c_args : common_c_args,
|
||||
dependencies : common_deps,
|
||||
install : false)
|
||||
|
||||
executable('performance-threaded', 'performance-threaded.c',
|
||||
c_args : common_c_args,
|
||||
dependencies : common_deps,
|
||||
install : false)
|
||||
418
tests/gobject/override.c
Normal file
418
tests/gobject/override.c
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* override.c: Closure override test program
|
||||
* Copyright (C) 2001, James Henstridge
|
||||
* Copyright (C) 2003, Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestOverride"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#undef VERBOSE
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "testcommon.h"
|
||||
|
||||
static guint foo_signal_id = 0;
|
||||
static guint bar_signal_id = 0;
|
||||
static guint baz_signal_id = 0;
|
||||
|
||||
static GType test_i_get_type (void);
|
||||
static GType test_a_get_type (void);
|
||||
static GType test_b_get_type (void);
|
||||
static GType test_c_get_type (void);
|
||||
|
||||
static void record (const gchar *str);
|
||||
|
||||
#define TEST_TYPE_I (test_i_get_type ())
|
||||
|
||||
typedef struct _TestI TestI;
|
||||
typedef struct _TestIClass TestIClass;
|
||||
|
||||
struct _TestIClass
|
||||
{
|
||||
GTypeInterface base_iface;
|
||||
};
|
||||
|
||||
static void
|
||||
test_i_foo (TestI *self)
|
||||
{
|
||||
record ("TestI::foo");
|
||||
}
|
||||
|
||||
static void
|
||||
test_i_default_init (gpointer g_class)
|
||||
{
|
||||
foo_signal_id = g_signal_newv ("foo",
|
||||
TEST_TYPE_I,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
g_cclosure_new(G_CALLBACK(test_i_foo),
|
||||
NULL, NULL),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0, NULL);
|
||||
}
|
||||
|
||||
static DEFINE_IFACE (TestI, test_i, NULL, test_i_default_init)
|
||||
|
||||
#define TEST_TYPE_A (test_a_get_type())
|
||||
|
||||
typedef struct _TestA TestA;
|
||||
typedef struct _TestAClass TestAClass;
|
||||
|
||||
struct _TestA {
|
||||
GObject parent;
|
||||
};
|
||||
struct _TestAClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* bar) (TestA *self);
|
||||
};
|
||||
|
||||
static void
|
||||
test_a_foo (TestI *self)
|
||||
{
|
||||
GValue args[1] = { G_VALUE_INIT };
|
||||
|
||||
record ("TestA::foo");
|
||||
|
||||
g_value_init (&args[0], TEST_TYPE_A);
|
||||
g_value_set_object (&args[0], self);
|
||||
|
||||
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
|
||||
g_signal_chain_from_overridden (args, NULL);
|
||||
|
||||
g_value_unset (&args[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_a_bar (TestA *self)
|
||||
{
|
||||
record ("TestA::bar");
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_a_baz (TestA *self,
|
||||
GObject *object,
|
||||
gpointer pointer)
|
||||
{
|
||||
record ("TestA::baz");
|
||||
|
||||
g_assert (object == G_OBJECT (self));
|
||||
g_assert (GPOINTER_TO_INT (pointer) == 23);
|
||||
|
||||
return g_strdup ("TestA::baz");
|
||||
}
|
||||
|
||||
static void
|
||||
test_a_class_init (TestAClass *class)
|
||||
{
|
||||
class->bar = test_a_bar;
|
||||
|
||||
bar_signal_id = g_signal_new ("bar",
|
||||
TEST_TYPE_A,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestAClass, bar),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0, NULL);
|
||||
|
||||
baz_signal_id = g_signal_new_class_handler ("baz",
|
||||
TEST_TYPE_A,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_CALLBACK (test_a_baz),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_STRING__OBJECT_POINTER,
|
||||
G_TYPE_STRING, 2,
|
||||
G_TYPE_OBJECT,
|
||||
G_TYPE_POINTER);
|
||||
}
|
||||
|
||||
static void
|
||||
test_a_interface_init (TestIClass *iface)
|
||||
{
|
||||
g_signal_override_class_closure (foo_signal_id,
|
||||
TEST_TYPE_A,
|
||||
g_cclosure_new (G_CALLBACK (test_a_foo),
|
||||
NULL, NULL));
|
||||
}
|
||||
|
||||
static DEFINE_TYPE_FULL (TestA, test_a,
|
||||
test_a_class_init, NULL, NULL,
|
||||
G_TYPE_OBJECT,
|
||||
INTERFACE (test_a_interface_init, TEST_TYPE_I))
|
||||
|
||||
#define TEST_TYPE_B (test_b_get_type())
|
||||
|
||||
typedef struct _TestB TestB;
|
||||
typedef struct _TestBClass TestBClass;
|
||||
|
||||
struct _TestB {
|
||||
TestA parent;
|
||||
};
|
||||
struct _TestBClass {
|
||||
TestAClass parent_class;
|
||||
};
|
||||
|
||||
static void
|
||||
test_b_foo (TestI *self)
|
||||
{
|
||||
GValue args[1] = { G_VALUE_INIT };
|
||||
|
||||
record ("TestB::foo");
|
||||
|
||||
g_value_init (&args[0], TEST_TYPE_A);
|
||||
g_value_set_object (&args[0], self);
|
||||
|
||||
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
|
||||
g_signal_chain_from_overridden (args, NULL);
|
||||
|
||||
g_value_unset (&args[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_b_bar (TestA *self)
|
||||
{
|
||||
GValue args[1] = { G_VALUE_INIT };
|
||||
|
||||
record ("TestB::bar");
|
||||
|
||||
g_value_init (&args[0], TEST_TYPE_A);
|
||||
g_value_set_object (&args[0], self);
|
||||
|
||||
g_assert (g_signal_get_invocation_hint (self)->signal_id == bar_signal_id);
|
||||
g_signal_chain_from_overridden (args, NULL);
|
||||
|
||||
g_value_unset (&args[0]);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_b_baz (TestA *self,
|
||||
GObject *object,
|
||||
gpointer pointer)
|
||||
{
|
||||
gchar *retval = NULL;
|
||||
|
||||
record ("TestB::baz");
|
||||
|
||||
g_assert (object == G_OBJECT (self));
|
||||
g_assert (GPOINTER_TO_INT (pointer) == 23);
|
||||
|
||||
g_signal_chain_from_overridden_handler (self, object, pointer, &retval);
|
||||
|
||||
if (retval)
|
||||
{
|
||||
gchar *tmp = g_strconcat (retval , ",TestB::baz", NULL);
|
||||
g_free (retval);
|
||||
retval = tmp;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
test_b_class_init (TestBClass *class)
|
||||
{
|
||||
g_signal_override_class_closure (foo_signal_id,
|
||||
TEST_TYPE_B,
|
||||
g_cclosure_new (G_CALLBACK (test_b_foo),
|
||||
NULL, NULL));
|
||||
g_signal_override_class_closure (bar_signal_id,
|
||||
TEST_TYPE_B,
|
||||
g_cclosure_new (G_CALLBACK (test_b_bar),
|
||||
NULL, NULL));
|
||||
g_signal_override_class_handler ("baz",
|
||||
TEST_TYPE_B,
|
||||
G_CALLBACK (test_b_baz));
|
||||
}
|
||||
|
||||
static DEFINE_TYPE (TestB, test_b,
|
||||
test_b_class_init, NULL, NULL,
|
||||
TEST_TYPE_A)
|
||||
|
||||
#define TEST_TYPE_C (test_c_get_type())
|
||||
|
||||
typedef struct _TestC TestC;
|
||||
typedef struct _TestCClass TestCClass;
|
||||
|
||||
struct _TestC {
|
||||
TestB parent;
|
||||
};
|
||||
struct _TestCClass {
|
||||
TestBClass parent_class;
|
||||
};
|
||||
|
||||
static void
|
||||
test_c_foo (TestI *self)
|
||||
{
|
||||
GValue args[1] = { G_VALUE_INIT };
|
||||
|
||||
record ("TestC::foo");
|
||||
|
||||
g_value_init (&args[0], TEST_TYPE_A);
|
||||
g_value_set_object (&args[0], self);
|
||||
|
||||
g_assert (g_signal_get_invocation_hint (self)->signal_id == foo_signal_id);
|
||||
g_signal_chain_from_overridden (args, NULL);
|
||||
|
||||
g_value_unset (&args[0]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_c_bar (TestA *self)
|
||||
{
|
||||
GValue args[1] = { G_VALUE_INIT };
|
||||
|
||||
record ("TestC::bar");
|
||||
|
||||
g_value_init (&args[0], TEST_TYPE_A);
|
||||
g_value_set_object (&args[0], self);
|
||||
|
||||
g_assert (g_signal_get_invocation_hint (self)->signal_id == bar_signal_id);
|
||||
g_signal_chain_from_overridden (args, NULL);
|
||||
|
||||
g_value_unset (&args[0]);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
test_c_baz (TestA *self,
|
||||
GObject *object,
|
||||
gpointer pointer)
|
||||
{
|
||||
gchar *retval = NULL;
|
||||
|
||||
record ("TestC::baz");
|
||||
|
||||
g_assert (object == G_OBJECT (self));
|
||||
g_assert (GPOINTER_TO_INT (pointer) == 23);
|
||||
|
||||
g_signal_chain_from_overridden_handler (self, object, pointer, &retval);
|
||||
|
||||
if (retval)
|
||||
{
|
||||
gchar *tmp = g_strconcat (retval , ",TestC::baz", NULL);
|
||||
g_free (retval);
|
||||
retval = tmp;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
test_c_class_init (TestBClass *class)
|
||||
{
|
||||
g_signal_override_class_closure (foo_signal_id,
|
||||
TEST_TYPE_C,
|
||||
g_cclosure_new (G_CALLBACK (test_c_foo),
|
||||
NULL, NULL));
|
||||
g_signal_override_class_closure (bar_signal_id,
|
||||
TEST_TYPE_C,
|
||||
g_cclosure_new (G_CALLBACK (test_c_bar),
|
||||
NULL, NULL));
|
||||
g_signal_override_class_handler ("baz",
|
||||
TEST_TYPE_C,
|
||||
G_CALLBACK (test_c_baz));
|
||||
}
|
||||
|
||||
|
||||
static DEFINE_TYPE (TestC, test_c,
|
||||
test_c_class_init, NULL, NULL,
|
||||
TEST_TYPE_B)
|
||||
|
||||
static GString *test_string = NULL;
|
||||
gboolean failed = FALSE;
|
||||
|
||||
static void
|
||||
record (const gchar *str)
|
||||
{
|
||||
if (test_string->len)
|
||||
g_string_append_c (test_string, ',');
|
||||
g_string_append (test_string, str);
|
||||
}
|
||||
|
||||
static void
|
||||
test (GType type,
|
||||
const gchar *signal,
|
||||
const gchar *expected,
|
||||
const gchar *expected_retval)
|
||||
{
|
||||
GObject *self = g_object_new (type, NULL);
|
||||
|
||||
test_string = g_string_new (NULL);
|
||||
|
||||
if (strcmp (signal, "baz"))
|
||||
{
|
||||
g_signal_emit_by_name (self, signal);
|
||||
}
|
||||
else
|
||||
{
|
||||
gchar *ret;
|
||||
|
||||
g_signal_emit_by_name (self, signal, self, GINT_TO_POINTER (23), &ret);
|
||||
|
||||
if (strcmp (ret, expected_retval) != 0)
|
||||
failed = TRUE;
|
||||
|
||||
g_free (ret);
|
||||
}
|
||||
|
||||
#ifndef VERBOSE
|
||||
if (strcmp (test_string->str, expected) != 0)
|
||||
#endif
|
||||
{
|
||||
g_printerr ("*** emitting %s on a %s instance\n"
|
||||
" Expecting: %s\n"
|
||||
" Got: %s\n",
|
||||
signal, g_type_name (type),
|
||||
expected,
|
||||
test_string->str);
|
||||
|
||||
if (strcmp (test_string->str, expected) != 0)
|
||||
failed = TRUE;
|
||||
}
|
||||
|
||||
g_string_free (test_string, TRUE);
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
test (TEST_TYPE_A, "foo", "TestA::foo,TestI::foo", NULL);
|
||||
test (TEST_TYPE_A, "bar", "TestA::bar", NULL);
|
||||
test (TEST_TYPE_A, "baz", "TestA::baz", "TestA::baz");
|
||||
|
||||
test (TEST_TYPE_B, "foo", "TestB::foo,TestA::foo,TestI::foo", NULL);
|
||||
test (TEST_TYPE_B, "bar", "TestB::bar,TestA::bar", NULL);
|
||||
test (TEST_TYPE_B, "baz", "TestB::baz,TestA::baz", "TestA::baz,TestB::baz");
|
||||
|
||||
test (TEST_TYPE_C, "foo", "TestC::foo,TestB::foo,TestA::foo,TestI::foo", NULL);
|
||||
test (TEST_TYPE_C, "bar", "TestC::bar,TestB::bar,TestA::bar", NULL);
|
||||
test (TEST_TYPE_C, "baz", "TestC::baz,TestB::baz,TestA::baz", "TestA::baz,TestB::baz,TestC::baz");
|
||||
|
||||
return failed ? 1 : 0;
|
||||
}
|
||||
375
tests/gobject/performance-threaded.c
Normal file
375
tests/gobject/performance-threaded.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2009 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <glib-object.h>
|
||||
#include "testcommon.h"
|
||||
|
||||
#define DEFAULT_TEST_TIME 2 /* seconds */
|
||||
|
||||
static GType
|
||||
simple_register_class (const char *name, GType parent, ...)
|
||||
{
|
||||
GInterfaceInfo interface_info = { NULL, NULL, NULL };
|
||||
va_list args;
|
||||
GType type, interface;
|
||||
|
||||
va_start (args, parent);
|
||||
type = g_type_register_static_simple (parent, name, sizeof (GObjectClass),
|
||||
NULL, parent == G_TYPE_INTERFACE ? 0 : sizeof (GObject), NULL, 0);
|
||||
for (;;)
|
||||
{
|
||||
interface = va_arg (args, GType);
|
||||
if (interface == 0)
|
||||
break;
|
||||
g_type_add_interface_static (type, interface, &interface_info);
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* test emulating liststore behavior for interface lookups */
|
||||
|
||||
static GType liststore;
|
||||
static GType liststore_interfaces[6];
|
||||
|
||||
static gpointer
|
||||
register_types (void)
|
||||
{
|
||||
static gsize inited = 0;
|
||||
if (g_once_init_enter (&inited))
|
||||
{
|
||||
liststore_interfaces[0] = simple_register_class ("GtkBuildable", G_TYPE_INTERFACE, 0);
|
||||
liststore_interfaces[1] = simple_register_class ("GtkTreeDragDest", G_TYPE_INTERFACE, 0);
|
||||
liststore_interfaces[2] = simple_register_class ("GtkTreeModel", G_TYPE_INTERFACE, 0);
|
||||
liststore_interfaces[3] = simple_register_class ("GtkTreeDragSource", G_TYPE_INTERFACE, 0);
|
||||
liststore_interfaces[4] = simple_register_class ("GtkTreeSortable", G_TYPE_INTERFACE, 0);
|
||||
liststore_interfaces[5] = simple_register_class ("UnrelatedInterface", G_TYPE_INTERFACE, 0);
|
||||
|
||||
liststore = simple_register_class ("GtkListStore", G_TYPE_OBJECT,
|
||||
liststore_interfaces[0], liststore_interfaces[1], liststore_interfaces[2],
|
||||
liststore_interfaces[3], liststore_interfaces[4], (GType) 0);
|
||||
|
||||
g_once_init_leave (&inited, 1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
liststore_is_a_run (gpointer data)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
g_assert (g_type_is_a (liststore, liststore_interfaces[0]));
|
||||
g_assert (g_type_is_a (liststore, liststore_interfaces[1]));
|
||||
g_assert (g_type_is_a (liststore, liststore_interfaces[2]));
|
||||
g_assert (g_type_is_a (liststore, liststore_interfaces[3]));
|
||||
g_assert (g_type_is_a (liststore, liststore_interfaces[4]));
|
||||
g_assert (!g_type_is_a (liststore, liststore_interfaces[5]));
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
liststore_get_class (void)
|
||||
{
|
||||
register_types ();
|
||||
return g_type_class_ref (liststore);
|
||||
}
|
||||
|
||||
static void
|
||||
liststore_interface_peek_run (gpointer klass)
|
||||
{
|
||||
guint i;
|
||||
gpointer iface;
|
||||
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[1]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[2]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[3]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[4]);
|
||||
g_assert (iface);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
liststore_interface_peek_same_run (gpointer klass)
|
||||
{
|
||||
guint i;
|
||||
gpointer iface;
|
||||
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
iface = g_type_interface_peek (klass, liststore_interfaces[0]);
|
||||
g_assert (iface);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* DUMB test doing nothing */
|
||||
|
||||
static gpointer
|
||||
no_setup (void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
no_run (gpointer data)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
no_reset (gpointer data)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
no_teardown (gpointer data)
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _PerformanceTest PerformanceTest;
|
||||
struct _PerformanceTest {
|
||||
const char *name;
|
||||
|
||||
gpointer (*setup) (void);
|
||||
void (*run) (gpointer data);
|
||||
void (*reset) (gpointer data);
|
||||
void (*teardown) (gpointer data);
|
||||
};
|
||||
|
||||
static const PerformanceTest tests[] = {
|
||||
{ "liststore-is-a",
|
||||
register_types,
|
||||
liststore_is_a_run,
|
||||
no_reset,
|
||||
no_teardown },
|
||||
{ "liststore-interface-peek",
|
||||
liststore_get_class,
|
||||
liststore_interface_peek_run,
|
||||
no_reset,
|
||||
g_type_class_unref },
|
||||
{ "liststore-interface-peek-same",
|
||||
liststore_get_class,
|
||||
liststore_interface_peek_same_run,
|
||||
no_reset,
|
||||
g_type_class_unref },
|
||||
#if 0
|
||||
{ "nothing",
|
||||
no_setup,
|
||||
no_run,
|
||||
no_reset,
|
||||
no_teardown }
|
||||
#endif
|
||||
};
|
||||
|
||||
static gboolean verbose = FALSE;
|
||||
static guint n_threads = 0;
|
||||
static gboolean list = FALSE;
|
||||
static int test_length = DEFAULT_TEST_TIME;
|
||||
|
||||
static GOptionEntry cmd_entries[] = {
|
||||
{"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
|
||||
"Print extra information", NULL},
|
||||
{"threads", 't', 0, G_OPTION_ARG_INT, &n_threads,
|
||||
"number of threads to run in parallel", NULL},
|
||||
{"seconds", 's', 0, G_OPTION_ARG_INT, &test_length,
|
||||
"Time to run each test in seconds", NULL},
|
||||
{"list", 'l', 0, G_OPTION_ARG_NONE, &list,
|
||||
"List all available tests and exit", NULL},
|
||||
G_OPTION_ENTRY_NULL
|
||||
};
|
||||
|
||||
static gpointer
|
||||
run_test_thread (gpointer user_data)
|
||||
{
|
||||
const PerformanceTest *test = user_data;
|
||||
gpointer data;
|
||||
double elapsed;
|
||||
GTimer *timer, *total;
|
||||
GArray *results;
|
||||
|
||||
total = g_timer_new ();
|
||||
g_timer_start (total);
|
||||
|
||||
/* Set up test */
|
||||
timer = g_timer_new ();
|
||||
data = test->setup ();
|
||||
results = g_array_new (FALSE, FALSE, sizeof (double));
|
||||
|
||||
/* Run the test */
|
||||
while (g_timer_elapsed (total, NULL) < test_length)
|
||||
{
|
||||
g_timer_reset (timer);
|
||||
g_timer_start (timer);
|
||||
test->run (data);
|
||||
g_timer_stop (timer);
|
||||
elapsed = g_timer_elapsed (timer, NULL);
|
||||
g_array_append_val (results, elapsed);
|
||||
test->reset (data);
|
||||
}
|
||||
|
||||
/* Tear down */
|
||||
test->teardown (data);
|
||||
g_timer_destroy (timer);
|
||||
g_timer_destroy (total);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
static int
|
||||
compare_doubles (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
double d = *(double *) a - *(double *) b;
|
||||
|
||||
if (d < 0)
|
||||
return -1;
|
||||
if (d > 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
print_results (GArray *array)
|
||||
{
|
||||
double min, max, avg;
|
||||
guint i;
|
||||
|
||||
g_array_sort (array, compare_doubles);
|
||||
|
||||
/* FIXME: discard outliers */
|
||||
|
||||
min = g_array_index (array, double, 0) * 1000;
|
||||
max = g_array_index (array, double, array->len - 1) * 1000;
|
||||
avg = 0;
|
||||
for (i = 0; i < array->len; i++)
|
||||
{
|
||||
avg += g_array_index (array, double, i);
|
||||
}
|
||||
avg = avg / array->len * 1000;
|
||||
|
||||
g_print (" %u runs, min/avg/max = %.3f/%.3f/%.3f ms\n", array->len, min, avg, max);
|
||||
}
|
||||
|
||||
static void
|
||||
run_test (const PerformanceTest *test)
|
||||
{
|
||||
GArray *results;
|
||||
|
||||
g_print ("Running test \"%s\"\n", test->name);
|
||||
|
||||
if (n_threads == 0) {
|
||||
results = run_test_thread ((gpointer) test);
|
||||
} else {
|
||||
guint i;
|
||||
GThread **threads;
|
||||
GArray *thread_results;
|
||||
|
||||
threads = g_new (GThread *, n_threads);
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
threads[i] = g_thread_create (run_test_thread, (gpointer) test, TRUE, NULL);
|
||||
g_assert (threads[i] != NULL);
|
||||
}
|
||||
|
||||
results = g_array_new (FALSE, FALSE, sizeof (double));
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
thread_results = g_thread_join (threads[i]);
|
||||
g_array_append_vals (results, thread_results->data, thread_results->len);
|
||||
g_array_free (thread_results, TRUE);
|
||||
}
|
||||
g_free (threads);
|
||||
}
|
||||
|
||||
print_results (results);
|
||||
g_array_free (results, TRUE);
|
||||
}
|
||||
|
||||
static const PerformanceTest *
|
||||
find_test (const char *name)
|
||||
{
|
||||
gsize i;
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
if (strcmp (tests[i].name, name) == 0)
|
||||
return &tests[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
const PerformanceTest *test;
|
||||
GOptionContext *context;
|
||||
GError *error = NULL;
|
||||
gsize i;
|
||||
|
||||
context = g_option_context_new ("GObject performance tests");
|
||||
g_option_context_add_main_entries (context, cmd_entries, NULL);
|
||||
if (!g_option_context_parse (context, &argc, &argv, &error))
|
||||
{
|
||||
g_printerr ("%s: %s\n", argv[0], error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (list)
|
||||
{
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
{
|
||||
g_print ("%s\n", tests[i].name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
int k;
|
||||
for (k = 1; k < argc; k++)
|
||||
{
|
||||
test = find_test (argv[k]);
|
||||
if (test)
|
||||
run_test (test);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < G_N_ELEMENTS (tests); i++)
|
||||
run_test (&tests[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
1060
tests/gobject/performance.c
Normal file
1060
tests/gobject/performance.c
Normal file
File diff suppressed because it is too large
Load diff
280
tests/gobject/references.c
Normal file
280
tests/gobject/references.c
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2005 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestReferences"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
/* This test tests weak and toggle references
|
||||
*/
|
||||
|
||||
static GObject *global_object;
|
||||
|
||||
static gboolean object_destroyed;
|
||||
static gboolean weak_ref1_notified;
|
||||
static gboolean weak_ref2_notified;
|
||||
static gboolean toggle_ref1_weakened;
|
||||
static gboolean toggle_ref1_strengthened;
|
||||
static gboolean toggle_ref2_weakened;
|
||||
static gboolean toggle_ref2_strengthened;
|
||||
static gboolean toggle_ref3_weakened;
|
||||
static gboolean toggle_ref3_strengthened;
|
||||
|
||||
/*
|
||||
* TestObject, a parent class for TestObject
|
||||
*/
|
||||
static GType test_object_get_type (void);
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
typedef struct _TestObject TestObject;
|
||||
typedef struct _TestObjectClass TestObjectClass;
|
||||
|
||||
struct _TestObject
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
struct _TestObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
test_object_finalize (GObject *object)
|
||||
{
|
||||
object_destroyed = TRUE;
|
||||
|
||||
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_class_init (TestObjectClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = test_object_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_init (TestObject *test_object)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
clear_flags (void)
|
||||
{
|
||||
object_destroyed = FALSE;
|
||||
weak_ref1_notified = FALSE;
|
||||
weak_ref2_notified = FALSE;
|
||||
toggle_ref1_weakened = FALSE;
|
||||
toggle_ref1_strengthened = FALSE;
|
||||
toggle_ref2_weakened = FALSE;
|
||||
toggle_ref2_strengthened = FALSE;
|
||||
toggle_ref3_weakened = FALSE;
|
||||
toggle_ref3_strengthened = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
weak_ref1 (gpointer data,
|
||||
GObject *object)
|
||||
{
|
||||
g_assert (object == global_object);
|
||||
g_assert (data == GUINT_TO_POINTER (42));
|
||||
|
||||
weak_ref1_notified = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
weak_ref2 (gpointer data,
|
||||
GObject *object)
|
||||
{
|
||||
g_assert (object == global_object);
|
||||
g_assert (data == GUINT_TO_POINTER (24));
|
||||
|
||||
weak_ref2_notified = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_ref1 (gpointer data,
|
||||
GObject *object,
|
||||
gboolean is_last_ref)
|
||||
{
|
||||
g_assert (object == global_object);
|
||||
g_assert (data == GUINT_TO_POINTER (42));
|
||||
|
||||
if (is_last_ref)
|
||||
toggle_ref1_weakened = TRUE;
|
||||
else
|
||||
toggle_ref1_strengthened = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_ref2 (gpointer data,
|
||||
GObject *object,
|
||||
gboolean is_last_ref)
|
||||
{
|
||||
g_assert (object == global_object);
|
||||
g_assert (data == GUINT_TO_POINTER (24));
|
||||
|
||||
if (is_last_ref)
|
||||
toggle_ref2_weakened = TRUE;
|
||||
else
|
||||
toggle_ref2_strengthened = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_ref3 (gpointer data,
|
||||
GObject *object,
|
||||
gboolean is_last_ref)
|
||||
{
|
||||
g_assert (object == global_object);
|
||||
g_assert (data == GUINT_TO_POINTER (34));
|
||||
|
||||
if (is_last_ref)
|
||||
{
|
||||
toggle_ref3_weakened = TRUE;
|
||||
g_object_remove_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
|
||||
}
|
||||
else
|
||||
toggle_ref3_strengthened = TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
/* Test basic weak reference operation
|
||||
*/
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (weak_ref1_notified == TRUE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
/* Test two weak references at once
|
||||
*/
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
||||
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (weak_ref1_notified == TRUE);
|
||||
g_assert (weak_ref2_notified == TRUE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
/* Test remove weak references
|
||||
*/
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
||||
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
|
||||
g_object_weak_unref (object, weak_ref1, GUINT_TO_POINTER (42));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (weak_ref1_notified == FALSE);
|
||||
g_assert (weak_ref2_notified == TRUE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
/* Test basic toggle reference operation
|
||||
*/
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (toggle_ref1_weakened == TRUE);
|
||||
g_assert (toggle_ref1_strengthened == FALSE);
|
||||
g_assert (object_destroyed == FALSE);
|
||||
|
||||
clear_flags ();
|
||||
g_object_ref (object);
|
||||
g_assert (toggle_ref1_weakened == FALSE);
|
||||
g_assert (toggle_ref1_strengthened == TRUE);
|
||||
g_assert (object_destroyed == FALSE);
|
||||
|
||||
g_object_unref (object);
|
||||
|
||||
clear_flags ();
|
||||
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
||||
g_assert (toggle_ref1_weakened == FALSE);
|
||||
g_assert (toggle_ref1_strengthened == FALSE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
/* Test two toggle references at once
|
||||
*/
|
||||
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
||||
g_object_add_toggle_ref (object, toggle_ref2, GUINT_TO_POINTER (24));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (toggle_ref1_weakened == FALSE);
|
||||
g_assert (toggle_ref1_strengthened == FALSE);
|
||||
g_assert (toggle_ref2_weakened == FALSE);
|
||||
g_assert (toggle_ref2_strengthened == FALSE);
|
||||
g_assert (object_destroyed == FALSE);
|
||||
|
||||
clear_flags ();
|
||||
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
||||
g_assert (toggle_ref1_weakened == FALSE);
|
||||
g_assert (toggle_ref1_strengthened == FALSE);
|
||||
g_assert (toggle_ref2_weakened == TRUE);
|
||||
g_assert (toggle_ref2_strengthened == FALSE);
|
||||
g_assert (object_destroyed == FALSE);
|
||||
|
||||
clear_flags ();
|
||||
/* Check that removing a toggle ref with %NULL data works fine. */
|
||||
g_object_remove_toggle_ref (object, toggle_ref2, NULL);
|
||||
g_assert (toggle_ref1_weakened == FALSE);
|
||||
g_assert (toggle_ref1_strengthened == FALSE);
|
||||
g_assert (toggle_ref2_weakened == FALSE);
|
||||
g_assert (toggle_ref2_strengthened == FALSE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
/* Test a toggle reference that removes itself
|
||||
*/
|
||||
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_object_add_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
|
||||
|
||||
clear_flags ();
|
||||
g_object_unref (object);
|
||||
g_assert (toggle_ref3_weakened == TRUE);
|
||||
g_assert (toggle_ref3_strengthened == FALSE);
|
||||
g_assert (object_destroyed == TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
tests/gobject/signals.c
Normal file
134
tests/gobject/signals.c
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2013 Red Hat, Inc.
|
||||
* Copy and pasted from accumulator.c and modified.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestSignals"
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_DISABLE_CHECKS
|
||||
#undef G_DISABLE_CAST_CHECKS
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "testcommon.h"
|
||||
|
||||
/* What this test tests is the behavior of signal disconnection
|
||||
* from within a signal handler for the signal being disconnected.
|
||||
*
|
||||
* The test demonstrates that signal handlers disconnected from a signal
|
||||
* from an earlier handler in the same emission will not be run.
|
||||
*
|
||||
* It also demonstrates that signal handlers connected from a signal
|
||||
* from an earlier handler in the same emission will not be run.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TestObject, a parent class for TestObject
|
||||
*/
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
typedef struct _TestObject TestObject;
|
||||
typedef struct _TestObjectClass TestObjectClass;
|
||||
static gboolean callback1_ran = FALSE, callback2_ran = FALSE, callback3_ran = FALSE, default_handler_ran = FALSE;
|
||||
|
||||
struct _TestObject
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
struct _TestObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*test_signal) (TestObject *object);
|
||||
};
|
||||
|
||||
static GType test_object_get_type (void);
|
||||
|
||||
static void
|
||||
test_object_real_signal (TestObject *object)
|
||||
{
|
||||
default_handler_ran = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_signal_callback3 (TestObject *object,
|
||||
gpointer data)
|
||||
{
|
||||
callback3_ran = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_signal_callback2 (TestObject *object,
|
||||
gpointer data)
|
||||
{
|
||||
callback2_ran = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_signal_callback1 (TestObject *object,
|
||||
gpointer data)
|
||||
{
|
||||
callback1_ran = TRUE;
|
||||
g_signal_handlers_disconnect_by_func (G_OBJECT (object),
|
||||
test_object_signal_callback2,
|
||||
data);
|
||||
g_signal_connect (object, "test-signal",
|
||||
G_CALLBACK (test_object_signal_callback3), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_object_class_init (TestObjectClass *class)
|
||||
{
|
||||
class->test_signal = test_object_real_signal;
|
||||
|
||||
g_signal_new ("test-signal",
|
||||
G_OBJECT_CLASS_TYPE (class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (TestObjectClass, test_signal),
|
||||
NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static DEFINE_TYPE(TestObject, test_object,
|
||||
test_object_class_init, NULL, NULL,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
TestObject *object;
|
||||
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_signal_connect (object, "test-signal",
|
||||
G_CALLBACK (test_object_signal_callback1), NULL);
|
||||
g_signal_connect (object, "test-signal",
|
||||
G_CALLBACK (test_object_signal_callback2), NULL);
|
||||
g_signal_emit_by_name (object, "test-signal");
|
||||
|
||||
g_assert (callback1_ran);
|
||||
g_assert (!callback2_ran);
|
||||
g_assert (!callback3_ran);
|
||||
g_assert (default_handler_ran);
|
||||
|
||||
g_object_unref (object);
|
||||
return 0;
|
||||
}
|
||||
84
tests/gobject/singleton.c
Normal file
84
tests/gobject/singleton.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2006 Imendio AB
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestSingleton"
|
||||
#include <glib-object.h>
|
||||
#include <string.h>
|
||||
|
||||
/* --- MySingleton class --- */
|
||||
typedef struct {
|
||||
GObject parent_instance;
|
||||
} MySingleton;
|
||||
typedef struct {
|
||||
GObjectClass parent_class;
|
||||
} MySingletonClass;
|
||||
|
||||
static GType my_singleton_get_type (void);
|
||||
#define MY_TYPE_SINGLETON (my_singleton_get_type ())
|
||||
#define MY_SINGLETON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MY_TYPE_SINGLETON, MySingleton))
|
||||
#define MY_IS_SINGLETON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MY_TYPE_SINGLETON))
|
||||
#define MY_SINGLETON_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), MY_TYPE_SINGLETON, MySingletonClass))
|
||||
#define MY_IS_SINGLETON_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), MY_TYPE_SINGLETON))
|
||||
#define MY_SINGLETON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MY_TYPE_SINGLETON, MySingletonClass))
|
||||
|
||||
G_DEFINE_TYPE (MySingleton, my_singleton, G_TYPE_OBJECT)
|
||||
|
||||
static MySingleton *the_one_and_only = NULL;
|
||||
|
||||
/* --- methods --- */
|
||||
static GObject*
|
||||
my_singleton_constructor (GType type,
|
||||
guint n_construct_properties,
|
||||
GObjectConstructParam *construct_properties)
|
||||
{
|
||||
if (the_one_and_only)
|
||||
return g_object_ref (G_OBJECT (the_one_and_only));
|
||||
else
|
||||
return G_OBJECT_CLASS (my_singleton_parent_class)->constructor (type, n_construct_properties, construct_properties);
|
||||
}
|
||||
|
||||
static void
|
||||
my_singleton_init (MySingleton *self)
|
||||
{
|
||||
g_assert (the_one_and_only == NULL);
|
||||
the_one_and_only = self;
|
||||
}
|
||||
|
||||
static void
|
||||
my_singleton_class_init (MySingletonClass *klass)
|
||||
{
|
||||
G_OBJECT_CLASS (klass)->constructor = my_singleton_constructor;
|
||||
}
|
||||
|
||||
/* --- test program --- */
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
MySingleton *singleton, *obj;
|
||||
|
||||
/* create the singleton */
|
||||
singleton = g_object_new (MY_TYPE_SINGLETON, NULL);
|
||||
g_assert (singleton != NULL);
|
||||
/* assert _singleton_ creation */
|
||||
obj = g_object_new (MY_TYPE_SINGLETON, NULL);
|
||||
g_assert (singleton == obj);
|
||||
g_object_unref (obj);
|
||||
/* shutdown */
|
||||
g_object_unref (singleton);
|
||||
return 0;
|
||||
}
|
||||
105
tests/gobject/testcommon.h
Normal file
105
tests/gobject/testcommon.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEST_COMMON_H__
|
||||
#define __TEST_COMMON_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define DEFINE_TYPE_FULL(name, prefix, \
|
||||
class_init, base_init, instance_init, \
|
||||
parent_type, interface_decl) \
|
||||
GType \
|
||||
prefix ## _get_type (void) \
|
||||
{ \
|
||||
static GType object_type = 0; \
|
||||
\
|
||||
if (!object_type) \
|
||||
{ \
|
||||
const GTypeInfo object_info = \
|
||||
{ \
|
||||
sizeof (name ## Class), \
|
||||
(GBaseInitFunc) base_init, \
|
||||
(GBaseFinalizeFunc) NULL, \
|
||||
(GClassInitFunc) class_init, \
|
||||
(GClassFinalizeFunc) NULL, \
|
||||
NULL, /* class_data */ \
|
||||
sizeof (name), \
|
||||
0, /* n_prelocs */ \
|
||||
(GInstanceInitFunc) instance_init, \
|
||||
(const GTypeValueTable *) NULL, \
|
||||
}; \
|
||||
\
|
||||
object_type = g_type_register_static (parent_type, \
|
||||
# name, \
|
||||
&object_info, 0); \
|
||||
interface_decl \
|
||||
} \
|
||||
\
|
||||
return object_type; \
|
||||
}
|
||||
|
||||
#define DEFINE_TYPE(name, prefix, \
|
||||
class_init, base_init, instance_init, \
|
||||
parent_type) \
|
||||
DEFINE_TYPE_FULL(name, prefix, class_init, base_init, \
|
||||
instance_init, parent_type, {})
|
||||
|
||||
#define DEFINE_IFACE(name, prefix, base_init, dflt_init) \
|
||||
GType \
|
||||
prefix ## _get_type (void) \
|
||||
{ \
|
||||
static GType iface_type = 0; \
|
||||
\
|
||||
if (!iface_type) \
|
||||
{ \
|
||||
const GTypeInfo iface_info = \
|
||||
{ \
|
||||
sizeof (name ## Class), \
|
||||
(GBaseInitFunc) base_init, \
|
||||
(GBaseFinalizeFunc) NULL, \
|
||||
(GClassInitFunc) dflt_init, \
|
||||
(GClassFinalizeFunc) NULL, \
|
||||
(gconstpointer) NULL, \
|
||||
(guint16) 0, \
|
||||
(guint16) 0, \
|
||||
(GInstanceInitFunc) NULL, \
|
||||
(const GTypeValueTable*) NULL, \
|
||||
}; \
|
||||
\
|
||||
iface_type = g_type_register_static (G_TYPE_INTERFACE, \
|
||||
# name, \
|
||||
&iface_info, 0); \
|
||||
} \
|
||||
return iface_type; \
|
||||
}
|
||||
|
||||
#define INTERFACE_FULL(type, init_func, iface_type) \
|
||||
{ \
|
||||
GInterfaceInfo const iface = \
|
||||
{ \
|
||||
(GInterfaceInitFunc) init_func, NULL, NULL \
|
||||
}; \
|
||||
\
|
||||
g_type_add_interface_static (type, iface_type, &iface); \
|
||||
}
|
||||
#define INTERFACE(init_func, iface_type) \
|
||||
INTERFACE_FULL(object_type, init_func, iface_type)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __TEST_COMMON_H__ */
|
||||
445
tests/gobject/testgobject.c
Normal file
445
tests/gobject/testgobject.c
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* Copyright (C) 2001 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "TestObject"
|
||||
#include <glib-object.h>
|
||||
|
||||
/* --- TestIface --- */
|
||||
#define TEST_TYPE_IFACE (test_iface_get_type ())
|
||||
#define TEST_IFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_IFACE, TestIface))
|
||||
#define TEST_IS_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_IFACE))
|
||||
#define TEST_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE, TestIfaceClass))
|
||||
typedef struct _TestIface TestIface;
|
||||
typedef struct _TestIfaceClass TestIfaceClass;
|
||||
struct _TestIfaceClass
|
||||
{
|
||||
GTypeInterface base_iface;
|
||||
void (*print_string) (TestIface *tiobj,
|
||||
const gchar *string);
|
||||
};
|
||||
static void iface_base_init (TestIfaceClass *iface);
|
||||
static void iface_base_finalize (TestIfaceClass *iface);
|
||||
static void print_foo (TestIface *tiobj,
|
||||
const gchar *string);
|
||||
static GType
|
||||
test_iface_get_type (void)
|
||||
{
|
||||
static GType test_iface_type = 0;
|
||||
|
||||
if (!test_iface_type)
|
||||
{
|
||||
const GTypeInfo test_iface_info =
|
||||
{
|
||||
sizeof (TestIfaceClass),
|
||||
(GBaseInitFunc) iface_base_init, /* base_init */
|
||||
(GBaseFinalizeFunc) iface_base_finalize, /* base_finalize */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_iface_type = g_type_register_static (G_TYPE_INTERFACE, "TestIface", &test_iface_info, 0);
|
||||
g_type_interface_add_prerequisite (test_iface_type, G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
return test_iface_type;
|
||||
}
|
||||
static guint iface_base_init_count = 0;
|
||||
static void
|
||||
iface_base_init (TestIfaceClass *iface)
|
||||
{
|
||||
iface_base_init_count++;
|
||||
if (iface_base_init_count == 1)
|
||||
{
|
||||
/* add signals here */
|
||||
}
|
||||
}
|
||||
static void
|
||||
iface_base_finalize (TestIfaceClass *iface)
|
||||
{
|
||||
iface_base_init_count--;
|
||||
if (iface_base_init_count == 0)
|
||||
{
|
||||
/* destroy signals here */
|
||||
}
|
||||
}
|
||||
static void
|
||||
print_foo (TestIface *tiobj,
|
||||
const gchar *string)
|
||||
{
|
||||
if (!string)
|
||||
string = "<NULL>";
|
||||
g_print ("Iface-FOO: \"%s\" from %p\n", string, tiobj);
|
||||
}
|
||||
static void
|
||||
test_object_test_iface_init (gpointer giface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
TestIfaceClass *iface = giface;
|
||||
|
||||
g_assert (iface_data == GUINT_TO_POINTER (42));
|
||||
|
||||
g_assert (G_TYPE_FROM_INTERFACE (iface) == TEST_TYPE_IFACE);
|
||||
|
||||
/* assert iface_base_init() was already called */
|
||||
g_assert (iface_base_init_count > 0);
|
||||
|
||||
/* initialize stuff */
|
||||
iface->print_string = print_foo;
|
||||
}
|
||||
static void
|
||||
iface_print_string (TestIface *tiobj,
|
||||
const gchar *string)
|
||||
{
|
||||
TestIfaceClass *iface;
|
||||
|
||||
g_return_if_fail (TEST_IS_IFACE (tiobj));
|
||||
g_return_if_fail (G_IS_OBJECT (tiobj)); /* ensured through prerequisite */
|
||||
|
||||
iface = TEST_IFACE_GET_CLASS (tiobj);
|
||||
g_object_ref (tiobj);
|
||||
iface->print_string (tiobj, string);
|
||||
g_object_unref (tiobj);
|
||||
}
|
||||
|
||||
|
||||
/* --- TestObject --- */
|
||||
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
||||
#define TEST_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_OBJECT, TestObject))
|
||||
#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjectClass))
|
||||
#define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJECT))
|
||||
#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
|
||||
#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjectClass))
|
||||
#define TEST_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TEST_TYPE_OBJECT, TestObjectPrivate))
|
||||
typedef struct _TestObject TestObject;
|
||||
typedef struct _TestObjectClass TestObjectClass;
|
||||
typedef struct _TestObjectPrivate TestObjectPrivate;
|
||||
struct _TestObject
|
||||
{
|
||||
GObject parent_instance;
|
||||
};
|
||||
struct _TestObjectClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
gchar* (*test_signal) (TestObject *tobject,
|
||||
TestIface *iface_object,
|
||||
gpointer tdata);
|
||||
};
|
||||
struct _TestObjectPrivate
|
||||
{
|
||||
int dummy1;
|
||||
gdouble dummy2;
|
||||
};
|
||||
static void test_object_class_init (TestObjectClass *class);
|
||||
static void test_object_init (TestObject *tobject);
|
||||
static gboolean test_signal_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer data);
|
||||
static gchar* test_object_test_signal (TestObject *tobject,
|
||||
TestIface *iface_object,
|
||||
gpointer tdata);
|
||||
static gint TestObject_private_offset;
|
||||
static inline gpointer
|
||||
test_object_get_instance_private (TestObject *self)
|
||||
{
|
||||
return (G_STRUCT_MEMBER_P (self, TestObject_private_offset));
|
||||
}
|
||||
|
||||
static GType
|
||||
test_object_get_type (void)
|
||||
{
|
||||
static GType test_object_type = 0;
|
||||
|
||||
if (!test_object_type)
|
||||
{
|
||||
const GTypeInfo test_object_info =
|
||||
{
|
||||
sizeof (TestObjectClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
(GClassInitFunc) test_object_class_init,
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (TestObject),
|
||||
5, /* n_preallocs */
|
||||
(GInstanceInitFunc) test_object_init,
|
||||
NULL
|
||||
};
|
||||
GInterfaceInfo iface_info = { test_object_test_iface_init, NULL, GUINT_TO_POINTER (42) };
|
||||
|
||||
test_object_type = g_type_register_static (G_TYPE_OBJECT, "TestObject", &test_object_info, 0);
|
||||
g_type_add_interface_static (test_object_type, TEST_TYPE_IFACE, &iface_info);
|
||||
|
||||
TestObject_private_offset =
|
||||
g_type_add_instance_private (test_object_type, sizeof (TestObjectPrivate));
|
||||
}
|
||||
|
||||
return test_object_type;
|
||||
}
|
||||
static void
|
||||
test_object_class_init (TestObjectClass *class)
|
||||
{
|
||||
/* GObjectClass *gobject_class = G_OBJECT_CLASS (class); */
|
||||
g_type_class_adjust_private_offset (class, &TestObject_private_offset);
|
||||
|
||||
class->test_signal = test_object_test_signal;
|
||||
|
||||
g_signal_new ("test-signal",
|
||||
G_OBJECT_CLASS_TYPE (class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
|
||||
G_STRUCT_OFFSET (TestObjectClass, test_signal),
|
||||
test_signal_accumulator, NULL,
|
||||
g_cclosure_marshal_STRING__OBJECT_POINTER,
|
||||
G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER);
|
||||
}
|
||||
static void
|
||||
test_object_init (TestObject *tobject)
|
||||
{
|
||||
TestObjectPrivate *priv = test_object_get_instance_private (tobject);
|
||||
|
||||
g_assert (priv);
|
||||
|
||||
priv->dummy1 = 54321;
|
||||
}
|
||||
/* Check to see if private data initialization in the
|
||||
* instance init function works.
|
||||
*/
|
||||
static void
|
||||
test_object_check_private_init (TestObject *tobject)
|
||||
{
|
||||
TestObjectPrivate *priv = test_object_get_instance_private (tobject);
|
||||
|
||||
g_print ("private data during initialization: %u == %u\n", priv->dummy1, 54321);
|
||||
g_assert (priv->dummy1 == 54321);
|
||||
}
|
||||
static gboolean
|
||||
test_signal_accumulator (GSignalInvocationHint *ihint,
|
||||
GValue *return_accu,
|
||||
const GValue *handler_return,
|
||||
gpointer data)
|
||||
{
|
||||
const gchar *accu_string = g_value_get_string (return_accu);
|
||||
const gchar *new_string = g_value_get_string (handler_return);
|
||||
gchar *result_string;
|
||||
|
||||
if (accu_string)
|
||||
result_string = g_strconcat (accu_string, new_string, NULL);
|
||||
else if (new_string)
|
||||
result_string = g_strdup (new_string);
|
||||
else
|
||||
result_string = NULL;
|
||||
|
||||
g_value_take_string (return_accu, result_string);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
static gchar*
|
||||
test_object_test_signal (TestObject *tobject,
|
||||
TestIface *iface_object,
|
||||
gpointer tdata)
|
||||
{
|
||||
g_message ("::test_signal default_handler called");
|
||||
|
||||
g_return_val_if_fail (TEST_IS_IFACE (iface_object), NULL);
|
||||
|
||||
return g_strdup ("<default_handler>");
|
||||
}
|
||||
|
||||
|
||||
/* --- TestIface for DerivedObject --- */
|
||||
static void
|
||||
print_bar (TestIface *tiobj,
|
||||
const gchar *string)
|
||||
{
|
||||
TestIfaceClass *parent_iface;
|
||||
|
||||
g_return_if_fail (TEST_IS_IFACE (tiobj));
|
||||
|
||||
if (!string)
|
||||
string = "<NULL>";
|
||||
g_print ("Iface-BAR: \"%s\" from %p\n", string, tiobj);
|
||||
|
||||
g_print ("chaining: ");
|
||||
parent_iface = g_type_interface_peek_parent (TEST_IFACE_GET_CLASS (tiobj));
|
||||
parent_iface->print_string (tiobj, string);
|
||||
|
||||
g_assert (g_type_interface_peek_parent (parent_iface) == NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
derived_object_test_iface_init (gpointer giface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
TestIfaceClass *iface = giface;
|
||||
|
||||
g_assert (iface_data == GUINT_TO_POINTER (87));
|
||||
|
||||
g_assert (G_TYPE_FROM_INTERFACE (iface) == TEST_TYPE_IFACE);
|
||||
|
||||
/* assert test_object_test_iface_init() was already called */
|
||||
g_assert (iface->print_string == print_foo);
|
||||
|
||||
/* override stuff */
|
||||
iface->print_string = print_bar;
|
||||
}
|
||||
|
||||
|
||||
/* --- DerivedObject --- */
|
||||
#define DERIVED_TYPE_OBJECT (derived_object_get_type ())
|
||||
#define DERIVED_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), DERIVED_TYPE_OBJECT, DerivedObject))
|
||||
#define DERIVED_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DERIVED_TYPE_OBJECT, DerivedObjectClass))
|
||||
#define DERIVED_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), DERIVED_TYPE_OBJECT))
|
||||
#define DERIVED_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DERIVED_TYPE_OBJECT))
|
||||
#define DERIVED_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DERIVED_TYPE_OBJECT, DerivedObjectClass))
|
||||
#define DERIVED_OBJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DERIVED_TYPE_OBJECT, DerivedObjectPrivate))
|
||||
typedef struct _DerivedObject DerivedObject;
|
||||
typedef struct _TestObjectClass DerivedObjectClass;
|
||||
typedef struct _DerivedObjectPrivate DerivedObjectPrivate;
|
||||
struct _DerivedObject
|
||||
{
|
||||
TestObject parent_instance;
|
||||
int dummy1;
|
||||
int dummy2;
|
||||
};
|
||||
struct _DerivedObjectPrivate
|
||||
{
|
||||
char dummy;
|
||||
};
|
||||
static void derived_object_class_init (DerivedObjectClass *class);
|
||||
static void derived_object_init (DerivedObject *dobject);
|
||||
static gint DerivedObject_private_offset;
|
||||
static inline gpointer
|
||||
derived_object_get_instance_private (DerivedObject *self)
|
||||
{
|
||||
return (G_STRUCT_MEMBER_P (self, DerivedObject_private_offset));
|
||||
}
|
||||
static GType
|
||||
derived_object_get_type (void)
|
||||
{
|
||||
static GType derived_object_type = 0;
|
||||
|
||||
if (!derived_object_type)
|
||||
{
|
||||
const GTypeInfo derived_object_info =
|
||||
{
|
||||
sizeof (DerivedObjectClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
(GClassInitFunc) derived_object_class_init,
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (DerivedObject),
|
||||
5, /* n_preallocs */
|
||||
(GInstanceInitFunc) derived_object_init,
|
||||
NULL
|
||||
};
|
||||
GInterfaceInfo iface_info = { derived_object_test_iface_init, NULL, GUINT_TO_POINTER (87) };
|
||||
|
||||
derived_object_type = g_type_register_static (TEST_TYPE_OBJECT, "DerivedObject", &derived_object_info, 0);
|
||||
g_type_add_interface_static (derived_object_type, TEST_TYPE_IFACE, &iface_info);
|
||||
DerivedObject_private_offset =
|
||||
g_type_add_instance_private (derived_object_type, sizeof (DerivedObjectPrivate));
|
||||
}
|
||||
|
||||
return derived_object_type;
|
||||
}
|
||||
static void
|
||||
derived_object_class_init (DerivedObjectClass *class)
|
||||
{
|
||||
g_type_class_adjust_private_offset (class, &DerivedObject_private_offset);
|
||||
}
|
||||
static void
|
||||
derived_object_init (DerivedObject *dobject)
|
||||
{
|
||||
TestObjectPrivate *test_priv;
|
||||
DerivedObjectPrivate *derived_priv;
|
||||
|
||||
derived_priv = derived_object_get_instance_private (dobject);
|
||||
|
||||
g_assert (derived_priv);
|
||||
|
||||
test_priv = test_object_get_instance_private (TEST_OBJECT (dobject));
|
||||
|
||||
g_assert (test_priv);
|
||||
}
|
||||
|
||||
/* --- main --- */
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
GTypeInfo info = { 0, };
|
||||
GTypeFundamentalInfo finfo = { 0, };
|
||||
GType type;
|
||||
TestObject *sigarg;
|
||||
DerivedObject *dobject;
|
||||
TestObjectPrivate *priv;
|
||||
gchar *string = NULL;
|
||||
|
||||
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
||||
G_LOG_LEVEL_WARNING |
|
||||
G_LOG_LEVEL_CRITICAL);
|
||||
|
||||
/* test new fundamentals */
|
||||
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST) == g_type_fundamental_next ());
|
||||
type = g_type_register_fundamental (g_type_fundamental_next (), "FooShadow1", &info, &finfo, 0);
|
||||
g_assert (type == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
|
||||
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1) == g_type_fundamental_next ());
|
||||
type = g_type_register_fundamental (g_type_fundamental_next (), "FooShadow2", &info, &finfo, 0);
|
||||
g_assert (type == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1));
|
||||
g_assert (G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 2) == g_type_fundamental_next ());
|
||||
g_assert (g_type_from_name ("FooShadow1") == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST));
|
||||
g_assert (g_type_from_name ("FooShadow2") == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1));
|
||||
|
||||
/* to test past class initialization interface setups, create the class here */
|
||||
g_type_class_ref (TEST_TYPE_OBJECT);
|
||||
|
||||
dobject = g_object_new (DERIVED_TYPE_OBJECT, NULL);
|
||||
test_object_check_private_init (TEST_OBJECT (dobject));
|
||||
|
||||
sigarg = g_object_new (TEST_TYPE_OBJECT, NULL);
|
||||
|
||||
g_print ("MAIN: emit test-signal:\n");
|
||||
g_signal_emit_by_name (dobject, "test-signal", sigarg, NULL, &string);
|
||||
g_message ("signal return: \"%s\"", string);
|
||||
g_assert_cmpstr (string, ==, "<default_handler><default_handler><default_handler>");
|
||||
g_free (string);
|
||||
|
||||
g_print ("MAIN: call iface print-string on test and derived object:\n");
|
||||
iface_print_string (TEST_IFACE (sigarg), "iface-string-from-test-type");
|
||||
iface_print_string (TEST_IFACE (dobject), "iface-string-from-derived-type");
|
||||
|
||||
priv = test_object_get_instance_private (TEST_OBJECT (dobject));
|
||||
g_print ("private data after initialization: %u == %u\n", priv->dummy1, 54321);
|
||||
g_assert (priv->dummy1 == 54321);
|
||||
|
||||
g_object_unref (sigarg);
|
||||
g_object_unref (dobject);
|
||||
|
||||
g_message ("%s done", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
4
tests/gobject/testmarshal.list
Normal file
4
tests/gobject/testmarshal.list
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Marshallers used in tests
|
||||
BOOLEAN:INT
|
||||
STRING:INT
|
||||
VARIANT:POINTER
|
||||
66
tests/gobject/testmodule.c
Normal file
66
tests/gobject/testmodule.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* testmodule.c: Dummy dynamic type module
|
||||
* Copyright (C) 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "testmodule.h"
|
||||
#include "testcommon.h"
|
||||
|
||||
static gboolean test_module_load (GTypeModule *module);
|
||||
static void test_module_unload (GTypeModule *module);
|
||||
|
||||
static void
|
||||
test_module_class_init (TestModuleClass *class)
|
||||
{
|
||||
GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
|
||||
|
||||
module_class->load = test_module_load;
|
||||
module_class->unload = test_module_unload;
|
||||
}
|
||||
|
||||
DEFINE_TYPE (TestModule, test_module,
|
||||
test_module_class_init, NULL, NULL,
|
||||
G_TYPE_TYPE_MODULE)
|
||||
|
||||
static gboolean
|
||||
test_module_load (GTypeModule *module)
|
||||
{
|
||||
TestModule *test_module = TEST_MODULE (module);
|
||||
|
||||
test_module->register_func (module);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_module_unload (GTypeModule *module)
|
||||
{
|
||||
}
|
||||
|
||||
GTypeModule *
|
||||
test_module_new (TestModuleRegisterFunc register_func)
|
||||
{
|
||||
TestModule *test_module = g_object_new (TEST_TYPE_MODULE, NULL);
|
||||
GTypeModule *module = G_TYPE_MODULE (test_module);
|
||||
|
||||
test_module->register_func = register_func;
|
||||
|
||||
/* Register the types initially */
|
||||
g_type_module_use (module);
|
||||
g_type_module_unuse (module);
|
||||
|
||||
return G_TYPE_MODULE (module);
|
||||
}
|
||||
55
tests/gobject/testmodule.h
Normal file
55
tests/gobject/testmodule.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* GObject - GLib Type, Object, Parameter and Signal Library
|
||||
* testmodule.h: Dummy dynamic type module
|
||||
* Copyright (C) 2003 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEST_MODULE_H__
|
||||
#define __TEST_MODULE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _TestModule TestModule;
|
||||
typedef struct _TestModuleClass TestModuleClass;
|
||||
|
||||
#define TEST_TYPE_MODULE (test_module_get_type ())
|
||||
#define TEST_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), TEST_TYPE_MODULE, TestModule))
|
||||
#define TEST_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), TEST_TYPE_MODULE, TestModuleClass))
|
||||
#define TEST_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), TEST_TYPE_MODULE))
|
||||
#define TEST_IS_MODULE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), TEST_TYPE_MODULE))
|
||||
#define TEST_MODULE_GET_CLASS(module) (G_TYPE_INSTANCE_GET_CLASS ((module), TEST_TYPE_MODULE, TestModuleClass))
|
||||
|
||||
typedef void (*TestModuleRegisterFunc) (GTypeModule *module);
|
||||
|
||||
struct _TestModule
|
||||
{
|
||||
GTypeModule parent_instance;
|
||||
|
||||
TestModuleRegisterFunc register_func;
|
||||
};
|
||||
|
||||
struct _TestModuleClass
|
||||
{
|
||||
GTypeModuleClass parent_class;
|
||||
};
|
||||
|
||||
GType test_module_get_type (void);
|
||||
GTypeModule *test_module_new (TestModuleRegisterFunc register_func);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __TEST_MODULE_H__ */
|
||||
228
tests/gobject/timeloop-closure.c
Normal file
228
tests/gobject/timeloop-closure.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
static int n_children = 3;
|
||||
static int n_active_children;
|
||||
static int n_iters = 10000;
|
||||
static GMainLoop *loop;
|
||||
|
||||
static void
|
||||
io_pipe (GIOChannel **channels)
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (pipe(fds) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
channels[0] = g_io_channel_unix_new (fds[0]);
|
||||
channels[1] = g_io_channel_unix_new (fds[1]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
read_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_read = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_read < len)
|
||||
{
|
||||
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
|
||||
if (err)
|
||||
{
|
||||
if (err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
}
|
||||
else if (count == 0)
|
||||
return FALSE;
|
||||
|
||||
bytes_read += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_written = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_written < len)
|
||||
{
|
||||
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
|
||||
if (err && err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
|
||||
bytes_written += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
run_child (GIOChannel *in_channel, GIOChannel *out_channel)
|
||||
{
|
||||
int i;
|
||||
int val = 1;
|
||||
GTimer *timer = g_timer_new();
|
||||
|
||||
for (i = 0; i < n_iters; i++)
|
||||
{
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
read_all (in_channel, (char *)&val, sizeof (val));
|
||||
}
|
||||
|
||||
val = 0;
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
|
||||
val = g_timer_elapsed (timer, NULL) * 1000;
|
||||
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
g_timer_destroy (timer);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
input_callback (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int val;
|
||||
GIOChannel *dest = (GIOChannel *)data;
|
||||
|
||||
if (!read_all (source, (char *)&val, sizeof(val)))
|
||||
{
|
||||
fprintf (stderr, "Unexpected EOF\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (val)
|
||||
{
|
||||
write_all (dest, (char *)&val, sizeof(val));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_io_channel_close (source);
|
||||
g_io_channel_close (dest);
|
||||
|
||||
n_active_children--;
|
||||
if (n_active_children == 0)
|
||||
g_main_loop_quit (loop);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_child (void)
|
||||
{
|
||||
int pid, errsv;
|
||||
GIOChannel *in_channels[2];
|
||||
GIOChannel *out_channels[2];
|
||||
GSource *source;
|
||||
|
||||
io_pipe (in_channels);
|
||||
io_pipe (out_channels);
|
||||
|
||||
pid = fork ();
|
||||
errsv = errno;
|
||||
|
||||
if (pid > 0) /* Parent */
|
||||
{
|
||||
g_io_channel_close (in_channels[0]);
|
||||
g_io_channel_close (out_channels[1]);
|
||||
|
||||
source = g_io_create_watch (out_channels[0], G_IO_IN | G_IO_HUP);
|
||||
g_source_set_closure (source,
|
||||
g_cclosure_new (G_CALLBACK (input_callback), in_channels[1],
|
||||
(GClosureNotify)g_io_channel_unref));
|
||||
g_source_attach (source, NULL);
|
||||
g_source_unref (source);
|
||||
|
||||
g_io_channel_unref (in_channels[0]);
|
||||
g_io_channel_unref (out_channels[0]);
|
||||
g_io_channel_unref (out_channels[1]);
|
||||
|
||||
}
|
||||
else if (pid == 0) /* Child */
|
||||
{
|
||||
g_io_channel_close (in_channels[1]);
|
||||
g_io_channel_close (out_channels[0]);
|
||||
|
||||
setsid ();
|
||||
|
||||
run_child (in_channels[0], out_channels[1]);
|
||||
}
|
||||
else /* Error */
|
||||
{
|
||||
fprintf (stderr, "Cannot fork: %s\n", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
difftimeval (struct timeval *old, struct timeval *new)
|
||||
{
|
||||
return
|
||||
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct rusage old_usage;
|
||||
struct rusage new_usage;
|
||||
|
||||
if (argc > 1)
|
||||
n_children = atoi(argv[1]);
|
||||
|
||||
if (argc > 2)
|
||||
n_iters = atoi(argv[2]);
|
||||
|
||||
printf ("Children: %d Iters: %d\n", n_children, n_iters);
|
||||
|
||||
n_active_children = n_children;
|
||||
for (i = 0; i < n_children; i++)
|
||||
create_child ();
|
||||
|
||||
getrusage (RUSAGE_SELF, &old_usage);
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (loop);
|
||||
getrusage (RUSAGE_SELF, &new_usage);
|
||||
|
||||
printf ("Elapsed user: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
|
||||
printf ("Elapsed system: %g\n",
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("Elapsed total: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("total / iteration: %g\n",
|
||||
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
|
||||
(n_iters * n_children));
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
75
tests/libmoduletestplugin_a.c
Normal file
75
tests/libmoduletestplugin_a.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* libgplugin_a.c - test plugin for testgmodule
|
||||
* Copyright (C) 1998 Tim Janik
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GLib Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <gmodule.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
G_MODULE_EXPORT void gplugin_a_func (void);
|
||||
G_MODULE_EXPORT void gplugin_clash_func (void);
|
||||
G_MODULE_EXPORT void g_clash_func (void);
|
||||
G_MODULE_EXPORT void gplugin_say_boo_func (void);
|
||||
G_MODULE_EXPORT void gplugin_a_module_func (GModule *module);
|
||||
|
||||
G_MODULE_EXPORT gchar* gplugin_a_state;
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_a_func (void)
|
||||
{
|
||||
gplugin_a_state = "Hello world";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_clash_func (void)
|
||||
{
|
||||
gplugin_a_state = "plugin clash";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
g_clash_func (void)
|
||||
{
|
||||
gplugin_a_state = "global clash";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_say_boo_func (void)
|
||||
{
|
||||
gplugin_a_state = "BOOH";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_a_module_func (GModule *module)
|
||||
{
|
||||
void *f = NULL;
|
||||
|
||||
if (!g_module_symbol (module, "gplugin_say_boo_func", &f ))
|
||||
{
|
||||
g_print ("error: %s\n", g_module_error ());
|
||||
exit (1);
|
||||
}
|
||||
|
||||
((void(*)(void)) f) ();
|
||||
}
|
||||
76
tests/libmoduletestplugin_b.c
Normal file
76
tests/libmoduletestplugin_b.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* libgplugin_b.c - test plugin for testgmodule
|
||||
* Copyright (C) 1998 Tim Janik
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GLib Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <gmodule.h>
|
||||
|
||||
G_MODULE_EXPORT gchar* gplugin_b_state;
|
||||
|
||||
G_MODULE_EXPORT const gchar* g_module_check_init (GModule *module);
|
||||
G_MODULE_EXPORT void g_module_unload (GModule *module);
|
||||
|
||||
G_MODULE_EXPORT void gplugin_b_func (void);
|
||||
G_MODULE_EXPORT void gplugin_clash_func (void);
|
||||
G_MODULE_EXPORT void g_clash_func (void);
|
||||
G_MODULE_EXPORT void gplugin_say_boo_func (void);
|
||||
|
||||
G_MODULE_EXPORT const gchar*
|
||||
g_module_check_init (GModule *module)
|
||||
{
|
||||
gplugin_b_state = "check-init";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
g_module_unload (GModule *module)
|
||||
{
|
||||
gplugin_b_state = "unloaded";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_b_func (void)
|
||||
{
|
||||
gplugin_b_state = "Hello world";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_clash_func (void)
|
||||
{
|
||||
gplugin_b_state = "plugin clash";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
g_clash_func (void)
|
||||
{
|
||||
gplugin_b_state = "global clash";
|
||||
}
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
gplugin_say_boo_func (void)
|
||||
{
|
||||
gplugin_b_state = "BOOH";
|
||||
}
|
||||
442
tests/mainloop-test.c
Normal file
442
tests/mainloop-test.c
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <fcntl.h> /* For _O_BINARY used by pipe() macro */
|
||||
#include <io.h> /* for _pipe() */
|
||||
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
|
||||
#endif
|
||||
|
||||
#define ITERS 10000
|
||||
#define INCREMENT 10
|
||||
#define NTHREADS 4
|
||||
#define NCRAWLERS 4
|
||||
#define CRAWLER_TIMEOUT_RANGE 40
|
||||
#define RECURSER_TIMEOUT 50
|
||||
|
||||
/* The partial ordering between the context array mutex and
|
||||
* crawler array mutex is that the crawler array mutex cannot
|
||||
* be locked while the context array mutex is locked
|
||||
*/
|
||||
GPtrArray *context_array;
|
||||
GMutex context_array_mutex;
|
||||
GCond context_array_cond;
|
||||
|
||||
GMainLoop *main_loop;
|
||||
|
||||
G_LOCK_DEFINE_STATIC (crawler_array_lock);
|
||||
GPtrArray *crawler_array;
|
||||
|
||||
typedef struct _AddrData AddrData;
|
||||
typedef struct _TestData TestData;
|
||||
|
||||
struct _AddrData
|
||||
{
|
||||
GMainLoop *loop;
|
||||
GIOChannel *dest;
|
||||
gint count;
|
||||
};
|
||||
|
||||
struct _TestData
|
||||
{
|
||||
gint current_val;
|
||||
gint iters;
|
||||
GIOChannel *in;
|
||||
};
|
||||
|
||||
static void cleanup_crawlers (GMainContext *context);
|
||||
|
||||
static gboolean
|
||||
read_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_read = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_read < len)
|
||||
{
|
||||
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
|
||||
if (err)
|
||||
{
|
||||
if (err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
}
|
||||
else if (count == 0)
|
||||
return FALSE;
|
||||
|
||||
bytes_read += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_written = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_written < len)
|
||||
{
|
||||
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
|
||||
if (err && err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
|
||||
bytes_written += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
adder_callback (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
|
||||
char result[32] = { 0, };
|
||||
|
||||
AddrData *addr_data = data;
|
||||
|
||||
if (!read_all (source, buf1, 32) ||
|
||||
!read_all (source, buf2, 32))
|
||||
{
|
||||
g_main_loop_quit (addr_data->loop);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sprintf (result, "%d", atoi(buf1) + atoi(buf2));
|
||||
write_all (addr_data->dest, result, 32);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timeout_callback (gpointer data)
|
||||
{
|
||||
AddrData *addr_data = data;
|
||||
|
||||
addr_data->count++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
adder_thread (gpointer data)
|
||||
{
|
||||
GMainContext *context;
|
||||
GSource *adder_source;
|
||||
GSource *timeout_source;
|
||||
|
||||
GIOChannel **channels = data;
|
||||
AddrData addr_data;
|
||||
|
||||
context = g_main_context_new ();
|
||||
|
||||
g_mutex_lock (&context_array_mutex);
|
||||
|
||||
g_ptr_array_add (context_array, context);
|
||||
|
||||
if (context_array->len == NTHREADS)
|
||||
g_cond_broadcast (&context_array_cond);
|
||||
|
||||
g_mutex_unlock (&context_array_mutex);
|
||||
|
||||
addr_data.dest = channels[1];
|
||||
addr_data.loop = g_main_loop_new (context, FALSE);
|
||||
addr_data.count = 0;
|
||||
|
||||
adder_source = g_io_create_watch (channels[0], G_IO_IN | G_IO_HUP);
|
||||
g_source_set_static_name (adder_source, "Adder I/O");
|
||||
g_source_set_callback (adder_source, (GSourceFunc)adder_callback, &addr_data, NULL);
|
||||
g_source_attach (adder_source, context);
|
||||
g_source_unref (adder_source);
|
||||
|
||||
timeout_source = g_timeout_source_new (10);
|
||||
g_source_set_static_name (timeout_source, "Adder timeout");
|
||||
g_source_set_callback (timeout_source, (GSourceFunc)timeout_callback, &addr_data, NULL);
|
||||
g_source_set_priority (timeout_source, G_PRIORITY_HIGH);
|
||||
g_source_attach (timeout_source, context);
|
||||
g_source_unref (timeout_source);
|
||||
|
||||
g_main_loop_run (addr_data.loop);
|
||||
|
||||
g_io_channel_unref (channels[0]);
|
||||
g_io_channel_unref (channels[1]);
|
||||
|
||||
g_free (channels);
|
||||
|
||||
g_main_loop_unref (addr_data.loop);
|
||||
|
||||
#ifdef VERBOSE
|
||||
g_print ("Timeout run %d times\n", addr_data.count);
|
||||
#endif
|
||||
|
||||
g_mutex_lock (&context_array_mutex);
|
||||
g_ptr_array_remove (context_array, context);
|
||||
if (context_array->len == 0)
|
||||
g_main_loop_quit (main_loop);
|
||||
g_mutex_unlock (&context_array_mutex);
|
||||
|
||||
cleanup_crawlers (context);
|
||||
g_main_context_unref (context);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
io_pipe (GIOChannel **channels)
|
||||
{
|
||||
gint fds[2];
|
||||
|
||||
if (pipe(fds) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_warning ("Cannot create pipe %s", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
channels[0] = g_io_channel_unix_new (fds[0]);
|
||||
channels[1] = g_io_channel_unix_new (fds[1]);
|
||||
|
||||
g_io_channel_set_close_on_unref (channels[0], TRUE);
|
||||
g_io_channel_set_close_on_unref (channels[1], TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
do_add (GIOChannel *in, gint a, gint b)
|
||||
{
|
||||
char buf1[32] = { 0, };
|
||||
char buf2[32] = { 0, };
|
||||
|
||||
sprintf (buf1, "%d", a);
|
||||
sprintf (buf2, "%d", b);
|
||||
|
||||
write_all (in, buf1, 32);
|
||||
write_all (in, buf2, 32);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
adder_response (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
char result[32];
|
||||
TestData *test_data = data;
|
||||
|
||||
if (!read_all (source, result, 32))
|
||||
return FALSE;
|
||||
|
||||
test_data->current_val = atoi (result);
|
||||
test_data->iters--;
|
||||
|
||||
if (test_data->iters == 0)
|
||||
{
|
||||
if (test_data->current_val != ITERS * INCREMENT)
|
||||
{
|
||||
g_print ("Addition failed: %d != %d\n",
|
||||
test_data->current_val, ITERS * INCREMENT);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_io_channel_unref (source);
|
||||
g_io_channel_unref (test_data->in);
|
||||
|
||||
g_free (test_data);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
do_add (test_data->in, test_data->current_val, INCREMENT);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GThread *
|
||||
create_adder_thread (void)
|
||||
{
|
||||
GThread *thread;
|
||||
TestData *test_data;
|
||||
|
||||
GIOChannel *in_channels[2];
|
||||
GIOChannel *out_channels[2];
|
||||
|
||||
GIOChannel **sub_channels;
|
||||
|
||||
sub_channels = g_new (GIOChannel *, 2);
|
||||
|
||||
io_pipe (in_channels);
|
||||
io_pipe (out_channels);
|
||||
|
||||
sub_channels[0] = in_channels[0];
|
||||
sub_channels[1] = out_channels[1];
|
||||
|
||||
thread = g_thread_new ("adder", adder_thread, sub_channels);
|
||||
|
||||
test_data = g_new (TestData, 1);
|
||||
test_data->in = in_channels[1];
|
||||
test_data->current_val = 0;
|
||||
test_data->iters = ITERS;
|
||||
|
||||
g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
|
||||
adder_response, test_data);
|
||||
|
||||
do_add (test_data->in, test_data->current_val, INCREMENT);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
static void create_crawler (void);
|
||||
|
||||
static void
|
||||
remove_crawler (void)
|
||||
{
|
||||
GSource *other_source;
|
||||
|
||||
if (crawler_array->len > 0)
|
||||
{
|
||||
other_source = crawler_array->pdata[g_random_int_range (0, crawler_array->len)];
|
||||
g_source_destroy (other_source);
|
||||
g_assert (g_ptr_array_remove_fast (crawler_array, other_source));
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
crawler_callback (gpointer data)
|
||||
{
|
||||
GSource *source = data;
|
||||
|
||||
G_LOCK (crawler_array_lock);
|
||||
|
||||
if (!g_ptr_array_remove_fast (crawler_array, source))
|
||||
remove_crawler();
|
||||
|
||||
remove_crawler();
|
||||
G_UNLOCK (crawler_array_lock);
|
||||
|
||||
create_crawler();
|
||||
create_crawler();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
create_crawler (void)
|
||||
{
|
||||
GSource *source = g_timeout_source_new (g_random_int_range (0, CRAWLER_TIMEOUT_RANGE));
|
||||
g_source_set_static_name (source, "Crawler timeout");
|
||||
g_source_set_callback (source, (GSourceFunc)crawler_callback, source, NULL);
|
||||
|
||||
G_LOCK (crawler_array_lock);
|
||||
g_ptr_array_add (crawler_array, source);
|
||||
|
||||
g_mutex_lock (&context_array_mutex);
|
||||
g_source_attach (source, context_array->pdata[g_random_int_range (0, context_array->len)]);
|
||||
g_source_unref (source);
|
||||
g_mutex_unlock (&context_array_mutex);
|
||||
|
||||
G_UNLOCK (crawler_array_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_crawlers (GMainContext *context)
|
||||
{
|
||||
guint i;
|
||||
|
||||
G_LOCK (crawler_array_lock);
|
||||
for (i = 0; i < crawler_array->len; i++)
|
||||
{
|
||||
if (g_source_get_context (crawler_array->pdata[i]) == context)
|
||||
{
|
||||
g_source_destroy (g_ptr_array_remove_index (crawler_array, i));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
G_UNLOCK (crawler_array_lock);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
recurser_idle (gpointer data)
|
||||
{
|
||||
GMainContext *context = data;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
g_main_context_iteration (context, FALSE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
recurser_start (gpointer data)
|
||||
{
|
||||
GMainContext *context;
|
||||
GSource *source;
|
||||
|
||||
g_mutex_lock (&context_array_mutex);
|
||||
if (context_array->len > 0)
|
||||
{
|
||||
context = context_array->pdata[g_random_int_range (0, context_array->len)];
|
||||
source = g_idle_source_new ();
|
||||
g_source_set_static_name (source, "Recursing idle source");
|
||||
g_source_set_callback (source, recurser_idle, context, NULL);
|
||||
g_source_attach (source, context);
|
||||
g_source_unref (source);
|
||||
}
|
||||
g_mutex_unlock (&context_array_mutex);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gint i;
|
||||
GThread *threads[NTHREADS];
|
||||
|
||||
context_array = g_ptr_array_new ();
|
||||
|
||||
crawler_array = g_ptr_array_new ();
|
||||
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
threads[i] = create_adder_thread ();
|
||||
|
||||
/* Wait for all threads to start
|
||||
*/
|
||||
g_mutex_lock (&context_array_mutex);
|
||||
|
||||
while (context_array->len < NTHREADS)
|
||||
g_cond_wait (&context_array_cond, &context_array_mutex);
|
||||
|
||||
g_mutex_unlock (&context_array_mutex);
|
||||
|
||||
for (i = 0; i < NCRAWLERS; i++)
|
||||
create_crawler ();
|
||||
|
||||
g_timeout_add (RECURSER_TIMEOUT, recurser_start, NULL);
|
||||
|
||||
g_main_loop_run (main_loop);
|
||||
g_main_loop_unref (main_loop);
|
||||
|
||||
for (i = 0; i < NTHREADS; i++)
|
||||
g_thread_join (threads[i]);
|
||||
|
||||
g_ptr_array_unref (crawler_array);
|
||||
g_ptr_array_unref (context_array);
|
||||
|
||||
return 0;
|
||||
}
|
||||
321
tests/mapping-test.c
Normal file
321
tests/mapping-test.c
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 2005 Matthias Clasen
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "glib.h"
|
||||
#include "gstdio.h"
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef G_OS_WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
static gchar *dir, *global_filename, *global_displayname, *childname;
|
||||
|
||||
static gboolean stop = FALSE;
|
||||
|
||||
static gint parent_pid;
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
|
||||
static void
|
||||
handle_usr1 (int signum)
|
||||
{
|
||||
stop = TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
check_stop (gpointer data)
|
||||
{
|
||||
GMainLoop *loop = data;
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
stop = g_file_test ("STOP", G_FILE_TEST_EXISTS);
|
||||
#endif
|
||||
|
||||
if (stop)
|
||||
g_main_loop_quit (loop);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
write_or_die (const gchar *filename,
|
||||
const gchar *contents,
|
||||
gssize length)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gchar *displayname;
|
||||
|
||||
if (!g_file_set_contents (filename, contents, length, &error))
|
||||
{
|
||||
displayname = g_filename_display_name (childname);
|
||||
g_print ("failed to write '%s': %s\n",
|
||||
displayname, error->message);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
static GMappedFile *
|
||||
map_or_die (const gchar *filename,
|
||||
gboolean writable)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GMappedFile *map;
|
||||
gchar *displayname;
|
||||
|
||||
map = g_mapped_file_new (filename, writable, &error);
|
||||
if (!map)
|
||||
{
|
||||
displayname = g_filename_display_name (childname);
|
||||
g_print ("failed to map '%s' non-writable, shared: %s\n",
|
||||
displayname, error->message);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
signal_parent (gpointer data)
|
||||
{
|
||||
#ifndef G_OS_WIN32
|
||||
kill (parent_pid, SIGUSR1);
|
||||
#endif
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static int
|
||||
child_main (int argc, char *argv[])
|
||||
{
|
||||
GMappedFile *map;
|
||||
GMainLoop *loop;
|
||||
|
||||
parent_pid = atoi (argv[2]);
|
||||
map = map_or_die (global_filename, FALSE);
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
signal (SIGUSR1, handle_usr1);
|
||||
#endif
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_idle_add (check_stop, loop);
|
||||
g_idle_add (signal_parent, NULL);
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_message ("test_child_private: received parent signal");
|
||||
|
||||
write_or_die (childname,
|
||||
g_mapped_file_get_contents (map),
|
||||
g_mapped_file_get_length (map));
|
||||
|
||||
signal_parent (NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
test_mapping (void)
|
||||
{
|
||||
GMappedFile *map;
|
||||
|
||||
write_or_die (global_filename, "ABC", -1);
|
||||
|
||||
map = map_or_die (global_filename, FALSE);
|
||||
g_assert (g_mapped_file_get_length (map) == 3);
|
||||
g_mapped_file_free (map);
|
||||
|
||||
map = map_or_die (global_filename, TRUE);
|
||||
g_assert (g_mapped_file_get_length (map) == 3);
|
||||
g_mapped_file_free (map);
|
||||
g_message ("test_mapping: ok");
|
||||
}
|
||||
|
||||
static void
|
||||
test_private (void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GMappedFile *map;
|
||||
gchar *buffer;
|
||||
gsize len;
|
||||
|
||||
write_or_die (global_filename, "ABC", -1);
|
||||
map = map_or_die (global_filename, TRUE);
|
||||
|
||||
buffer = (gchar *)g_mapped_file_get_contents (map);
|
||||
buffer[0] = '1';
|
||||
buffer[1] = '2';
|
||||
buffer[2] = '3';
|
||||
g_mapped_file_free (map);
|
||||
|
||||
if (!g_file_get_contents (global_filename, &buffer, &len, &error))
|
||||
{
|
||||
g_print ("failed to read '%s': %s\n",
|
||||
global_displayname, error->message);
|
||||
exit (1);
|
||||
|
||||
}
|
||||
g_assert (len == 3);
|
||||
g_assert (strcmp (buffer, "ABC") == 0);
|
||||
g_free (buffer);
|
||||
|
||||
g_message ("test_private: ok");
|
||||
}
|
||||
|
||||
static void
|
||||
test_child_private (gchar *argv0)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GMappedFile *map;
|
||||
gchar *buffer;
|
||||
gsize len;
|
||||
gchar *child_argv[4];
|
||||
GPid child_pid;
|
||||
#ifndef G_OS_WIN32
|
||||
GMainLoop *loop;
|
||||
#endif
|
||||
gchar pid[100];
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
g_remove ("STOP");
|
||||
g_assert (!g_file_test ("STOP", G_FILE_TEST_EXISTS));
|
||||
#endif
|
||||
|
||||
write_or_die (global_filename, "ABC", -1);
|
||||
map = map_or_die (global_filename, TRUE);
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
signal (SIGUSR1, handle_usr1);
|
||||
#endif
|
||||
|
||||
g_snprintf (pid, sizeof(pid), "%d", getpid ());
|
||||
child_argv[0] = argv0;
|
||||
child_argv[1] = "mapchild";
|
||||
child_argv[2] = pid;
|
||||
child_argv[3] = NULL;
|
||||
if (!g_spawn_async (dir, child_argv, NULL,
|
||||
0, NULL, NULL, &child_pid, &error))
|
||||
{
|
||||
g_print ("failed to spawn child: %s\n",
|
||||
error->message);
|
||||
exit (1);
|
||||
}
|
||||
g_message ("test_child_private: child spawned");
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_idle_add (check_stop, loop);
|
||||
g_main_loop_run (loop);
|
||||
stop = FALSE;
|
||||
#else
|
||||
g_usleep (2000000);
|
||||
#endif
|
||||
|
||||
g_message ("test_child_private: received first child signal");
|
||||
|
||||
buffer = (gchar *)g_mapped_file_get_contents (map);
|
||||
buffer[0] = '1';
|
||||
buffer[1] = '2';
|
||||
buffer[2] = '3';
|
||||
g_mapped_file_free (map);
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
kill (child_pid, SIGUSR1);
|
||||
#else
|
||||
g_file_set_contents ("STOP", "Hey there\n", -1, NULL);
|
||||
#endif
|
||||
|
||||
#ifndef G_OS_WIN32
|
||||
g_idle_add (check_stop, loop);
|
||||
g_main_loop_run (loop);
|
||||
#else
|
||||
g_usleep (2000000);
|
||||
#endif
|
||||
|
||||
g_message ("test_child_private: received second child signal");
|
||||
|
||||
if (!g_file_get_contents (childname, &buffer, &len, &error))
|
||||
{
|
||||
gchar *name;
|
||||
|
||||
name = g_filename_display_name (childname);
|
||||
g_print ("failed to read '%s': %s\n", name, error->message);
|
||||
exit (1);
|
||||
}
|
||||
g_assert (len == 3);
|
||||
g_assert (strcmp (buffer, "ABC") == 0);
|
||||
g_free (buffer);
|
||||
|
||||
g_message ("test_child_private: ok");
|
||||
}
|
||||
|
||||
static int
|
||||
parent_main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
/* test mapping with various flag combinations */
|
||||
test_mapping ();
|
||||
|
||||
/* test private modification */
|
||||
test_private ();
|
||||
|
||||
/* test multiple clients, non-shared */
|
||||
test_child_private (argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
int ret;
|
||||
#ifndef G_OS_WIN32
|
||||
sigset_t sig_mask, old_mask;
|
||||
|
||||
sigemptyset (&sig_mask);
|
||||
sigaddset (&sig_mask, SIGUSR1);
|
||||
if (sigprocmask (SIG_UNBLOCK, &sig_mask, &old_mask) == 0)
|
||||
{
|
||||
if (sigismember (&old_mask, SIGUSR1))
|
||||
g_message ("SIGUSR1 was blocked, unblocking it");
|
||||
}
|
||||
#endif
|
||||
|
||||
dir = g_get_current_dir ();
|
||||
global_filename = g_build_filename (dir, "maptest", NULL);
|
||||
global_displayname = g_filename_display_name (global_filename);
|
||||
childname = g_build_filename (dir, "mapchild", NULL);
|
||||
|
||||
if (argc > 1)
|
||||
ret = child_main (argc, argv);
|
||||
else
|
||||
ret = parent_main (argc, argv);
|
||||
|
||||
g_free (childname);
|
||||
g_free (global_filename);
|
||||
g_free (global_displayname);
|
||||
g_free (dir);
|
||||
|
||||
return ret;
|
||||
}
|
||||
603
tests/memchunks.c
Normal file
603
tests/memchunks.c
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GLib Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MT safe
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
/* notes on macros:
|
||||
* if ENABLE_GC_FRIENDLY is defined, freed memory should be 0-wiped.
|
||||
*/
|
||||
|
||||
#define MEM_PROFILE_TABLE_SIZE 4096
|
||||
|
||||
#define MEM_AREA_SIZE 4L
|
||||
|
||||
static guint mem_chunk_recursion = 0;
|
||||
# define MEM_CHUNK_ROUTINE_COUNT() (mem_chunk_recursion)
|
||||
# define ENTER_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () + 1)
|
||||
# define LEAVE_MEM_CHUNK_ROUTINE() (mem_chunk_recursion = MEM_CHUNK_ROUTINE_COUNT () - 1)
|
||||
|
||||
/* --- old memchunk prototypes --- */
|
||||
GMemChunk* old_mem_chunk_new (const gchar *name,
|
||||
gulong atom_size,
|
||||
gulong area_size,
|
||||
gint type);
|
||||
void old_mem_chunk_destroy (GMemChunk *mem_chunk);
|
||||
gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk);
|
||||
gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_free (GMemChunk *mem_chunk,
|
||||
gpointer mem);
|
||||
void old_mem_chunk_clean (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_reset (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_print (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_info (void);
|
||||
|
||||
|
||||
/* --- MemChunks --- */
|
||||
#ifndef G_ALLOC_AND_FREE
|
||||
typedef struct _GAllocator GAllocator;
|
||||
typedef struct _GMemChunk GMemChunk;
|
||||
#define G_ALLOC_ONLY 1
|
||||
#define G_ALLOC_AND_FREE 2
|
||||
#endif
|
||||
|
||||
typedef struct _GFreeAtom GFreeAtom;
|
||||
typedef struct _GMemArea GMemArea;
|
||||
|
||||
struct _GFreeAtom
|
||||
{
|
||||
GFreeAtom *next;
|
||||
};
|
||||
|
||||
struct _GMemArea
|
||||
{
|
||||
GMemArea *next; /* the next mem area */
|
||||
GMemArea *prev; /* the previous mem area */
|
||||
gulong index; /* the current index into the "mem" array */
|
||||
gulong free; /* the number of free bytes in this mem area */
|
||||
gulong allocated; /* the number of atoms allocated from this area */
|
||||
gulong mark; /* is this mem area marked for deletion */
|
||||
gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated
|
||||
* the actual size of this array is determined by
|
||||
* the mem chunk "area_size". ANSI says that it
|
||||
* must be declared to be the maximum size it
|
||||
* can possibly be (even though the actual size
|
||||
* may be less).
|
||||
*/
|
||||
};
|
||||
|
||||
struct _GMemChunk
|
||||
{
|
||||
const gchar *name; /* name of this MemChunk...used for debugging output */
|
||||
gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
|
||||
gint num_mem_areas; /* the number of memory areas */
|
||||
gint num_marked_areas; /* the number of areas marked for deletion */
|
||||
guint atom_size; /* the size of an atom */
|
||||
gulong area_size; /* the size of a memory area */
|
||||
GMemArea *mem_area; /* the current memory area */
|
||||
GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */
|
||||
GMemArea *free_mem_area; /* the free area...which is about to be destroyed */
|
||||
GFreeAtom *free_atoms; /* the free atoms list */
|
||||
GTree *mem_tree; /* tree of mem areas sorted by memory address */
|
||||
GMemChunk *next; /* pointer to the next chunk */
|
||||
GMemChunk *prev; /* pointer to the previous chunk */
|
||||
};
|
||||
|
||||
|
||||
static gulong old_mem_chunk_compute_size (gulong size,
|
||||
gulong min_size) G_GNUC_CONST;
|
||||
static gint old_mem_chunk_area_compare (GMemArea *a,
|
||||
GMemArea *b);
|
||||
static gint old_mem_chunk_area_search (GMemArea *a,
|
||||
gchar *addr);
|
||||
|
||||
/* here we can't use StaticMutexes, as they depend upon a working
|
||||
* g_malloc, the same holds true for StaticPrivate
|
||||
*/
|
||||
static GMutex mem_chunks_lock;
|
||||
static GMemChunk *mem_chunks = NULL;
|
||||
|
||||
GMemChunk*
|
||||
old_mem_chunk_new (const gchar *name,
|
||||
gulong atom_size,
|
||||
gulong area_size,
|
||||
gint type)
|
||||
{
|
||||
GMemChunk *mem_chunk;
|
||||
gulong rarea_size;
|
||||
|
||||
g_return_val_if_fail (atom_size > 0, NULL);
|
||||
g_return_val_if_fail (area_size >= atom_size, NULL);
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
area_size = (area_size + atom_size - 1) / atom_size;
|
||||
area_size *= atom_size;
|
||||
|
||||
mem_chunk = g_new (GMemChunk, 1);
|
||||
mem_chunk->name = name;
|
||||
mem_chunk->type = type;
|
||||
mem_chunk->num_mem_areas = 0;
|
||||
mem_chunk->num_marked_areas = 0;
|
||||
mem_chunk->mem_area = NULL;
|
||||
mem_chunk->free_mem_area = NULL;
|
||||
mem_chunk->free_atoms = NULL;
|
||||
mem_chunk->mem_tree = NULL;
|
||||
mem_chunk->mem_areas = NULL;
|
||||
mem_chunk->atom_size = atom_size;
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
|
||||
|
||||
if (mem_chunk->atom_size % G_MEM_ALIGN)
|
||||
mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN);
|
||||
|
||||
rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
|
||||
rarea_size = old_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
|
||||
mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
|
||||
|
||||
g_mutex_lock (&mem_chunks_lock);
|
||||
mem_chunk->next = mem_chunks;
|
||||
mem_chunk->prev = NULL;
|
||||
if (mem_chunks)
|
||||
mem_chunks->prev = mem_chunk;
|
||||
mem_chunks = mem_chunk;
|
||||
g_mutex_unlock (&mem_chunks_lock);
|
||||
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
return mem_chunk;
|
||||
}
|
||||
|
||||
void
|
||||
old_mem_chunk_destroy (GMemChunk *mem_chunk)
|
||||
{
|
||||
GMemArea *mem_areas;
|
||||
GMemArea *temp_area;
|
||||
|
||||
g_return_if_fail (mem_chunk != NULL);
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
mem_areas = mem_chunk->mem_areas;
|
||||
while (mem_areas)
|
||||
{
|
||||
temp_area = mem_areas;
|
||||
mem_areas = mem_areas->next;
|
||||
g_free (temp_area);
|
||||
}
|
||||
|
||||
g_mutex_lock (&mem_chunks_lock);
|
||||
if (mem_chunk->next)
|
||||
mem_chunk->next->prev = mem_chunk->prev;
|
||||
if (mem_chunk->prev)
|
||||
mem_chunk->prev->next = mem_chunk->next;
|
||||
|
||||
if (mem_chunk == mem_chunks)
|
||||
mem_chunks = mem_chunks->next;
|
||||
g_mutex_unlock (&mem_chunks_lock);
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
g_tree_destroy (mem_chunk->mem_tree);
|
||||
|
||||
g_free (mem_chunk);
|
||||
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
}
|
||||
|
||||
gpointer
|
||||
old_mem_chunk_alloc (GMemChunk *mem_chunk)
|
||||
{
|
||||
GMemArea *temp_area;
|
||||
gpointer mem;
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
g_return_val_if_fail (mem_chunk != NULL, NULL);
|
||||
|
||||
while (mem_chunk->free_atoms)
|
||||
{
|
||||
/* Get the first piece of memory on the "free_atoms" list.
|
||||
* We can go ahead and destroy the list node we used to keep
|
||||
* track of it with and to update the "free_atoms" list to
|
||||
* point to its next element.
|
||||
*/
|
||||
mem = mem_chunk->free_atoms;
|
||||
mem_chunk->free_atoms = mem_chunk->free_atoms->next;
|
||||
|
||||
/* Determine which area this piece of memory is allocated from */
|
||||
temp_area = g_tree_search (mem_chunk->mem_tree,
|
||||
(GCompareFunc) old_mem_chunk_area_search,
|
||||
mem);
|
||||
|
||||
/* If the area has been marked, then it is being destroyed.
|
||||
* (ie marked to be destroyed).
|
||||
* We check to see if all of the segments on the free list that
|
||||
* reference this area have been removed. This occurs when
|
||||
* the amount of free memory is less than the allocatable size.
|
||||
* If the chunk should be freed, then we place it in the "free_mem_area".
|
||||
* This is so we make sure not to free the mem area here and then
|
||||
* allocate it again a few lines down.
|
||||
* If we don't allocate a chunk a few lines down then the "free_mem_area"
|
||||
* will be freed.
|
||||
* If there is already a "free_mem_area" then we'll just free this mem area.
|
||||
*/
|
||||
if (temp_area->mark)
|
||||
{
|
||||
/* Update the "free" memory available in that area */
|
||||
temp_area->free += mem_chunk->atom_size;
|
||||
|
||||
if (temp_area->free == mem_chunk->area_size)
|
||||
{
|
||||
if (temp_area == mem_chunk->mem_area)
|
||||
mem_chunk->mem_area = NULL;
|
||||
|
||||
if (mem_chunk->free_mem_area)
|
||||
{
|
||||
mem_chunk->num_mem_areas -= 1;
|
||||
|
||||
if (temp_area->next)
|
||||
temp_area->next->prev = temp_area->prev;
|
||||
if (temp_area->prev)
|
||||
temp_area->prev->next = temp_area->next;
|
||||
if (temp_area == mem_chunk->mem_areas)
|
||||
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
g_tree_remove (mem_chunk->mem_tree, temp_area);
|
||||
g_free (temp_area);
|
||||
}
|
||||
else
|
||||
mem_chunk->free_mem_area = temp_area;
|
||||
|
||||
mem_chunk->num_marked_areas -= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update the number of allocated atoms count.
|
||||
*/
|
||||
temp_area->allocated += 1;
|
||||
|
||||
/* The area wasn't marked...return the memory
|
||||
*/
|
||||
goto outa_here;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there isn't a current mem area or the current mem area is out of space
|
||||
* then allocate a new mem area. We'll first check and see if we can use
|
||||
* the "free_mem_area". Otherwise we'll just malloc the mem area.
|
||||
*/
|
||||
if ((!mem_chunk->mem_area) ||
|
||||
((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size))
|
||||
{
|
||||
if (mem_chunk->free_mem_area)
|
||||
{
|
||||
mem_chunk->mem_area = mem_chunk->free_mem_area;
|
||||
mem_chunk->free_mem_area = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ENABLE_GC_FRIENDLY
|
||||
mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
|
||||
MEM_AREA_SIZE +
|
||||
mem_chunk->area_size);
|
||||
#else /* !ENABLE_GC_FRIENDLY */
|
||||
mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
|
||||
MEM_AREA_SIZE +
|
||||
mem_chunk->area_size);
|
||||
#endif /* ENABLE_GC_FRIENDLY */
|
||||
|
||||
mem_chunk->num_mem_areas += 1;
|
||||
mem_chunk->mem_area->next = mem_chunk->mem_areas;
|
||||
mem_chunk->mem_area->prev = NULL;
|
||||
|
||||
if (mem_chunk->mem_areas)
|
||||
mem_chunk->mem_areas->prev = mem_chunk->mem_area;
|
||||
mem_chunk->mem_areas = mem_chunk->mem_area;
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area);
|
||||
}
|
||||
|
||||
mem_chunk->mem_area->index = 0;
|
||||
mem_chunk->mem_area->free = mem_chunk->area_size;
|
||||
mem_chunk->mem_area->allocated = 0;
|
||||
mem_chunk->mem_area->mark = 0;
|
||||
}
|
||||
|
||||
/* Get the memory and modify the state variables appropriately.
|
||||
*/
|
||||
mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index];
|
||||
mem_chunk->mem_area->index += mem_chunk->atom_size;
|
||||
mem_chunk->mem_area->free -= mem_chunk->atom_size;
|
||||
mem_chunk->mem_area->allocated += 1;
|
||||
|
||||
outa_here:
|
||||
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
gpointer
|
||||
old_mem_chunk_alloc0 (GMemChunk *mem_chunk)
|
||||
{
|
||||
gpointer mem;
|
||||
|
||||
mem = old_mem_chunk_alloc (mem_chunk);
|
||||
if (mem)
|
||||
{
|
||||
memset (mem, 0, mem_chunk->atom_size);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
void
|
||||
old_mem_chunk_free (GMemChunk *mem_chunk,
|
||||
gpointer mem)
|
||||
{
|
||||
GMemArea *temp_area;
|
||||
GFreeAtom *free_atom;
|
||||
|
||||
g_return_if_fail (mem_chunk != NULL);
|
||||
g_return_if_fail (mem != NULL);
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
#ifdef ENABLE_GC_FRIENDLY
|
||||
memset (mem, 0, mem_chunk->atom_size);
|
||||
#endif /* ENABLE_GC_FRIENDLY */
|
||||
|
||||
/* Don't do anything if this is an ALLOC_ONLY chunk
|
||||
*/
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
{
|
||||
/* Place the memory on the "free_atoms" list
|
||||
*/
|
||||
free_atom = (GFreeAtom*) mem;
|
||||
free_atom->next = mem_chunk->free_atoms;
|
||||
mem_chunk->free_atoms = free_atom;
|
||||
|
||||
temp_area = g_tree_search (mem_chunk->mem_tree,
|
||||
(GCompareFunc) old_mem_chunk_area_search,
|
||||
mem);
|
||||
|
||||
temp_area->allocated -= 1;
|
||||
|
||||
if (temp_area->allocated == 0)
|
||||
{
|
||||
temp_area->mark = 1;
|
||||
mem_chunk->num_marked_areas += 1;
|
||||
}
|
||||
}
|
||||
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
}
|
||||
|
||||
/* This doesn't free the free_area if there is one */
|
||||
void
|
||||
old_mem_chunk_clean (GMemChunk *mem_chunk)
|
||||
{
|
||||
GMemArea *mem_area;
|
||||
GFreeAtom *prev_free_atom;
|
||||
GFreeAtom *temp_free_atom;
|
||||
gpointer mem;
|
||||
|
||||
g_return_if_fail (mem_chunk != NULL);
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
{
|
||||
prev_free_atom = NULL;
|
||||
temp_free_atom = mem_chunk->free_atoms;
|
||||
|
||||
while (temp_free_atom)
|
||||
{
|
||||
mem = (gpointer) temp_free_atom;
|
||||
|
||||
mem_area = g_tree_search (mem_chunk->mem_tree,
|
||||
(GCompareFunc) old_mem_chunk_area_search,
|
||||
mem);
|
||||
|
||||
/* If this mem area is marked for destruction then delete the
|
||||
* area and list node and decrement the free mem.
|
||||
*/
|
||||
if (mem_area->mark)
|
||||
{
|
||||
if (prev_free_atom)
|
||||
prev_free_atom->next = temp_free_atom->next;
|
||||
else
|
||||
mem_chunk->free_atoms = temp_free_atom->next;
|
||||
temp_free_atom = temp_free_atom->next;
|
||||
|
||||
mem_area->free += mem_chunk->atom_size;
|
||||
if (mem_area->free == mem_chunk->area_size)
|
||||
{
|
||||
mem_chunk->num_mem_areas -= 1;
|
||||
mem_chunk->num_marked_areas -= 1;
|
||||
|
||||
if (mem_area->next)
|
||||
mem_area->next->prev = mem_area->prev;
|
||||
if (mem_area->prev)
|
||||
mem_area->prev->next = mem_area->next;
|
||||
if (mem_area == mem_chunk->mem_areas)
|
||||
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
|
||||
if (mem_area == mem_chunk->mem_area)
|
||||
mem_chunk->mem_area = NULL;
|
||||
|
||||
if (mem_chunk->type == G_ALLOC_AND_FREE)
|
||||
g_tree_remove (mem_chunk->mem_tree, mem_area);
|
||||
g_free (mem_area);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_free_atom = temp_free_atom;
|
||||
temp_free_atom = temp_free_atom->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
}
|
||||
|
||||
void
|
||||
old_mem_chunk_reset (GMemChunk *mem_chunk)
|
||||
{
|
||||
GMemArea *mem_areas;
|
||||
GMemArea *temp_area;
|
||||
|
||||
g_return_if_fail (mem_chunk != NULL);
|
||||
|
||||
ENTER_MEM_CHUNK_ROUTINE ();
|
||||
|
||||
mem_areas = mem_chunk->mem_areas;
|
||||
mem_chunk->num_mem_areas = 0;
|
||||
mem_chunk->mem_areas = NULL;
|
||||
mem_chunk->mem_area = NULL;
|
||||
|
||||
while (mem_areas)
|
||||
{
|
||||
temp_area = mem_areas;
|
||||
mem_areas = mem_areas->next;
|
||||
g_free (temp_area);
|
||||
}
|
||||
|
||||
mem_chunk->free_atoms = NULL;
|
||||
|
||||
if (mem_chunk->mem_tree)
|
||||
{
|
||||
g_tree_destroy (mem_chunk->mem_tree);
|
||||
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) old_mem_chunk_area_compare);
|
||||
}
|
||||
|
||||
LEAVE_MEM_CHUNK_ROUTINE ();
|
||||
}
|
||||
|
||||
void
|
||||
old_mem_chunk_print (GMemChunk *mem_chunk)
|
||||
{
|
||||
GMemArea *mem_areas;
|
||||
gulong mem;
|
||||
|
||||
g_return_if_fail (mem_chunk != NULL);
|
||||
|
||||
mem_areas = mem_chunk->mem_areas;
|
||||
mem = 0;
|
||||
|
||||
while (mem_areas)
|
||||
{
|
||||
mem += mem_chunk->area_size - mem_areas->free;
|
||||
mem_areas = mem_areas->next;
|
||||
}
|
||||
|
||||
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
|
||||
"%s: %ld bytes using %d mem areas",
|
||||
mem_chunk->name, mem, mem_chunk->num_mem_areas);
|
||||
}
|
||||
|
||||
void
|
||||
old_mem_chunk_info (void)
|
||||
{
|
||||
GMemChunk *mem_chunk;
|
||||
gint count;
|
||||
|
||||
count = 0;
|
||||
g_mutex_lock (&mem_chunks_lock);
|
||||
mem_chunk = mem_chunks;
|
||||
while (mem_chunk)
|
||||
{
|
||||
count += 1;
|
||||
mem_chunk = mem_chunk->next;
|
||||
}
|
||||
g_mutex_unlock (&mem_chunks_lock);
|
||||
|
||||
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count);
|
||||
|
||||
g_mutex_lock (&mem_chunks_lock);
|
||||
mem_chunk = mem_chunks;
|
||||
g_mutex_unlock (&mem_chunks_lock);
|
||||
|
||||
while (mem_chunk)
|
||||
{
|
||||
old_mem_chunk_print ((GMemChunk*) mem_chunk);
|
||||
mem_chunk = mem_chunk->next;
|
||||
}
|
||||
}
|
||||
|
||||
static gulong
|
||||
old_mem_chunk_compute_size (gulong size,
|
||||
gulong min_size)
|
||||
{
|
||||
gulong power_of_2;
|
||||
gulong lower, upper;
|
||||
|
||||
power_of_2 = 16;
|
||||
while (power_of_2 < size)
|
||||
power_of_2 <<= 1;
|
||||
|
||||
lower = power_of_2 >> 1;
|
||||
upper = power_of_2;
|
||||
|
||||
if (size - lower < upper - size && lower >= min_size)
|
||||
return lower;
|
||||
else
|
||||
return upper;
|
||||
}
|
||||
|
||||
static gint
|
||||
old_mem_chunk_area_compare (GMemArea *a,
|
||||
GMemArea *b)
|
||||
{
|
||||
if (a->mem > b->mem)
|
||||
return 1;
|
||||
else if (a->mem < b->mem)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint
|
||||
old_mem_chunk_area_search (GMemArea *a,
|
||||
gchar *addr)
|
||||
{
|
||||
if (a->mem <= addr)
|
||||
{
|
||||
if (addr < &a->mem[a->index])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
139
tests/meson.build
Normal file
139
tests/meson.build
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# tests
|
||||
|
||||
# Not entirely random of course, but at least it changes over time
|
||||
random_number = minor_version + meson.version().split('.').get(1).to_int()
|
||||
|
||||
test_env = environment()
|
||||
test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
|
||||
test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
|
||||
test_env.set('G_DEBUG', 'gc-friendly')
|
||||
test_env.set('MALLOC_CHECK_', '2')
|
||||
test_env.set('MALLOC_PERTURB_', '@0@'.format(random_number % 256))
|
||||
|
||||
test_cargs = ['-DG_LOG_DOMAIN="GLib"', '-UG_DISABLE_ASSERT']
|
||||
|
||||
subdir('gobject')
|
||||
subdir('refcount')
|
||||
|
||||
tests = {
|
||||
'gio-test' : {},
|
||||
'mainloop-test' : {},
|
||||
'mapping-test' : {},
|
||||
'onceinit' : {},
|
||||
'slice-threadinit' : {
|
||||
'dependencies' : [libgthread_dep],
|
||||
},
|
||||
'spawn-test' : {},
|
||||
'thread-test' : {},
|
||||
'threadpool-test' : {'suite' : ['slow']},
|
||||
'unicode-encoding' : {},
|
||||
'module-test-library' : {
|
||||
'dependencies' : [libgmodule_dep],
|
||||
'export_dynamic' : true,
|
||||
'source': 'module-test.c',
|
||||
'c_args': ['-DMODULE_TYPE="library"'],
|
||||
},
|
||||
'module-test-plugin' : {
|
||||
'dependencies' : [libgmodule_dep],
|
||||
'export_dynamic' : true,
|
||||
'source': 'module-test.c',
|
||||
'c_args': ['-DMODULE_TYPE="plugin"'],
|
||||
},
|
||||
}
|
||||
|
||||
test_extra_programs = {
|
||||
'slice-test' : {
|
||||
'extra_sources' : ['memchunks.c'],
|
||||
},
|
||||
'assert-msg-test' : {},
|
||||
}
|
||||
|
||||
if host_machine.system() != 'windows'
|
||||
tests += {
|
||||
'timeloop' : {},
|
||||
}
|
||||
else
|
||||
test_extra_programs += {
|
||||
'spawn-test-win32-gui' : {'gui_app' : true}
|
||||
}
|
||||
endif
|
||||
|
||||
if installed_tests_enabled
|
||||
install_data(
|
||||
'utf8.txt',
|
||||
install_dir : installed_tests_execdir,
|
||||
)
|
||||
endif
|
||||
|
||||
module_suffix = []
|
||||
# Keep the autotools convention for shared module suffix because GModule
|
||||
# depends on it: https://gitlab.gnome.org/GNOME/glib/issues/520
|
||||
if ['darwin', 'ios'].contains(host_machine.system())
|
||||
module_suffix = 'so'
|
||||
endif
|
||||
|
||||
foreach module : ['moduletestplugin_a', 'moduletestplugin_b']
|
||||
shared_module(module + '_plugin', 'lib@0@.c'.format(module),
|
||||
dependencies : [libglib_dep, libgmodule_dep],
|
||||
install_dir : installed_tests_execdir,
|
||||
install : installed_tests_enabled,
|
||||
name_suffix : module_suffix
|
||||
)
|
||||
shared_library(module + '_library', 'lib@0@.c'.format(module),
|
||||
dependencies : [libglib_dep, libgmodule_dep],
|
||||
install_dir : installed_tests_execdir,
|
||||
install : installed_tests_enabled,
|
||||
name_suffix : module_suffix
|
||||
)
|
||||
endforeach
|
||||
|
||||
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
|
||||
common_deps = [libm, thread_dep, libglib_dep]
|
||||
|
||||
foreach test_name, extra_args : tests
|
||||
source = extra_args.get('source', test_name + '.c')
|
||||
extra_sources = extra_args.get('extra_sources', [])
|
||||
install = installed_tests_enabled and extra_args.get('install', true)
|
||||
template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
|
||||
|
||||
if install
|
||||
test_conf = configuration_data()
|
||||
test_conf.set('installed_tests_dir', installed_tests_execdir)
|
||||
test_conf.set('program', test_name)
|
||||
test_conf.set('env', '')
|
||||
configure_file(
|
||||
input: template,
|
||||
output: test_name + '.test',
|
||||
install_dir: installed_tests_metadir,
|
||||
configuration: test_conf
|
||||
)
|
||||
endif
|
||||
|
||||
# FIXME? $(GLIB_DEBUG_FLAGS)
|
||||
exe = executable(test_name, [source, extra_sources],
|
||||
c_args : common_c_args + extra_args.get('c_args', []),
|
||||
dependencies : common_deps + extra_args.get('dependencies', []),
|
||||
export_dynamic : extra_args.get('export_dynamic', false),
|
||||
include_directories : extra_args.get('include_directories', []),
|
||||
install_dir: installed_tests_execdir,
|
||||
install: install,
|
||||
)
|
||||
|
||||
suite = ['glib'] + extra_args.get('suite', [])
|
||||
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
|
||||
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
|
||||
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
|
||||
endforeach
|
||||
|
||||
foreach program_name, extra_args : test_extra_programs
|
||||
source = extra_args.get('source', program_name + '.c')
|
||||
extra_sources = extra_args.get('extra_sources', [])
|
||||
install = installed_tests_enabled and extra_args.get('install', true)
|
||||
executable(program_name, [source, extra_sources],
|
||||
c_args : common_c_args,
|
||||
dependencies : common_deps + extra_args.get('dependencies', []),
|
||||
install_dir : installed_tests_execdir,
|
||||
install : install,
|
||||
gui_app : extra_args.get('gui_app', false),
|
||||
)
|
||||
endforeach
|
||||
214
tests/module-test.c
Normal file
214
tests/module-test.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/* module-test.c - test program for GMODULE
|
||||
* Copyright (C) 1998 Tim Janik
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GLib Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <gmodule.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define MODULE_FILENAME_PREFIX ""
|
||||
#else
|
||||
# define MODULE_FILENAME_PREFIX "lib"
|
||||
#endif
|
||||
|
||||
gchar* global_state;
|
||||
|
||||
G_MODULE_EXPORT void g_clash_func (void);
|
||||
|
||||
G_MODULE_EXPORT void
|
||||
g_clash_func (void)
|
||||
{
|
||||
global_state = "global clash";
|
||||
}
|
||||
|
||||
typedef void (*SimpleFunc) (void);
|
||||
typedef void (*GModuleFunc) (GModule *);
|
||||
|
||||
static gchar **gplugin_a_state;
|
||||
static gchar **gplugin_b_state;
|
||||
|
||||
static void
|
||||
compare (const gchar *desc, const gchar *expected, const gchar *found)
|
||||
{
|
||||
if (!expected && !found)
|
||||
return;
|
||||
|
||||
if (expected && found && strcmp (expected, found) == 0)
|
||||
return;
|
||||
|
||||
g_error ("error: %s state should have been \"%s\", but is \"%s\"",
|
||||
desc, expected ? expected : "NULL", found ? found : "NULL");
|
||||
}
|
||||
|
||||
static void
|
||||
test_states (const gchar *global, const gchar *gplugin_a,
|
||||
const gchar *gplugin_b)
|
||||
{
|
||||
compare ("global", global, global_state);
|
||||
compare ("Plugin A", gplugin_a, *gplugin_a_state);
|
||||
compare ("Plugin B", gplugin_b, *gplugin_b_state);
|
||||
|
||||
global_state = *gplugin_a_state = *gplugin_b_state = NULL;
|
||||
}
|
||||
|
||||
static SimpleFunc plugin_clash_func = NULL;
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
GModule *module_self, *module_a, *module_b;
|
||||
gchar *plugin_a, *plugin_b;
|
||||
SimpleFunc f_a, f_b, f_self;
|
||||
GModuleFunc gmod_f;
|
||||
GError *error = NULL;
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
if (!g_module_supported ())
|
||||
g_error ("dynamic modules not supported");
|
||||
|
||||
plugin_a = g_test_build_filename (G_TEST_BUILT, MODULE_FILENAME_PREFIX "moduletestplugin_a_" MODULE_TYPE, NULL);
|
||||
plugin_b = g_test_build_filename (G_TEST_BUILT, MODULE_FILENAME_PREFIX "moduletestplugin_b_" MODULE_TYPE, NULL);
|
||||
|
||||
/* module handles */
|
||||
|
||||
module_self = g_module_open_full (NULL, G_MODULE_BIND_LAZY, &error);
|
||||
g_assert_no_error (error);
|
||||
if (!module_self)
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
/* On Windows static compilation mode, glib API symbols are not
|
||||
* exported dynamically by definition. */
|
||||
#if !defined(G_PLATFORM_WIN32) || !defined(GLIB_STATIC_COMPILATION)
|
||||
if (!g_module_symbol (module_self, "g_module_close", (gpointer *) &f_self))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
#endif
|
||||
|
||||
module_a = g_module_open_full (plugin_a, G_MODULE_BIND_LAZY, &error);
|
||||
g_assert_no_error (error);
|
||||
if (!module_a)
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
module_b = g_module_open_full (plugin_b, G_MODULE_BIND_LAZY, &error);
|
||||
g_assert_no_error (error);
|
||||
if (!module_b)
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
/* get plugin state vars */
|
||||
|
||||
if (!g_module_symbol (module_a, "gplugin_a_state",
|
||||
(gpointer *) &gplugin_a_state))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
if (!g_module_symbol (module_b, "gplugin_b_state",
|
||||
(gpointer *) &gplugin_b_state))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, "check-init");
|
||||
|
||||
/* get plugin specific symbols and call them
|
||||
*/
|
||||
if (!g_module_symbol (module_a, "gplugin_a_func", (gpointer *) &f_a))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
if (!g_module_symbol (module_b, "gplugin_b_func", (gpointer *) &f_b))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
f_a ();
|
||||
test_states (NULL, "Hello world", NULL);
|
||||
|
||||
f_b ();
|
||||
test_states (NULL, NULL, "Hello world");
|
||||
|
||||
/* get and call globally clashing functions
|
||||
*/
|
||||
|
||||
if (!g_module_symbol (module_self, "g_clash_func", (gpointer *) &f_self))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
if (!g_module_symbol (module_a, "g_clash_func", (gpointer *) &f_a))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
if (!g_module_symbol (module_b, "g_clash_func", (gpointer *) &f_b))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
f_self ();
|
||||
test_states ("global clash", NULL, NULL);
|
||||
|
||||
f_a ();
|
||||
test_states (NULL, "global clash", NULL);
|
||||
|
||||
f_b ();
|
||||
test_states (NULL, NULL, "global clash");
|
||||
|
||||
/* get and call clashing plugin functions */
|
||||
|
||||
if (!g_module_symbol (module_a, "gplugin_clash_func", (gpointer *) &f_a))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
if (!g_module_symbol (module_b, "gplugin_clash_func", (gpointer *) &f_b))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
plugin_clash_func = f_a;
|
||||
plugin_clash_func ();
|
||||
test_states (NULL, "plugin clash", NULL);
|
||||
|
||||
plugin_clash_func = f_b;
|
||||
plugin_clash_func ();
|
||||
test_states (NULL, NULL, "plugin clash");
|
||||
|
||||
/* call gmodule function from A */
|
||||
|
||||
if (!g_module_symbol (module_a, "gplugin_a_module_func", (gpointer *) &gmod_f))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
test_states (NULL, NULL, NULL);
|
||||
|
||||
gmod_f (module_b);
|
||||
test_states (NULL, NULL, "BOOH");
|
||||
|
||||
gmod_f (module_a);
|
||||
test_states (NULL, "BOOH", NULL);
|
||||
|
||||
/* unload plugins */
|
||||
|
||||
if (!g_module_close (module_a))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
if (!g_module_close (module_b))
|
||||
g_error ("error: %s", g_module_error ());
|
||||
|
||||
g_free (plugin_a);
|
||||
g_free (plugin_b);
|
||||
g_module_close (module_self);
|
||||
return 0;
|
||||
}
|
||||
273
tests/onceinit.c
Normal file
273
tests/onceinit.c
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/* g_once_init_*() test
|
||||
* Copyright (C) 2007 Tim Janik
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
#include <glib.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define N_THREADS (13)
|
||||
|
||||
static GMutex tmutex;
|
||||
static GCond tcond;
|
||||
static int thread_call_count = 0; /* (atomic) */
|
||||
static char dummy_value = 'x';
|
||||
|
||||
static void
|
||||
assert_singleton_execution1 (void)
|
||||
{
|
||||
static int seen_execution = 0; /* (atomic) */
|
||||
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
|
||||
if (old_seen_execution != 0)
|
||||
g_error ("%s: function executed more than once", G_STRFUNC);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_singleton_execution2 (void)
|
||||
{
|
||||
static int seen_execution = 0; /* (atomic) */
|
||||
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
|
||||
if (old_seen_execution != 0)
|
||||
g_error ("%s: function executed more than once", G_STRFUNC);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_singleton_execution3 (void)
|
||||
{
|
||||
static int seen_execution = 0; /* (atomic) */
|
||||
int old_seen_execution = g_atomic_int_add (&seen_execution, 1);
|
||||
if (old_seen_execution != 0)
|
||||
g_error ("%s: function executed more than once", G_STRFUNC);
|
||||
}
|
||||
|
||||
static void
|
||||
initializer1 (void)
|
||||
{
|
||||
static gsize initialized = 0;
|
||||
if (g_once_init_enter (&initialized))
|
||||
{
|
||||
gsize initval = 42;
|
||||
assert_singleton_execution1();
|
||||
g_once_init_leave (&initialized, initval);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
initializer2 (void)
|
||||
{
|
||||
static gsize initialized = 0;
|
||||
if (g_once_init_enter (&initialized))
|
||||
{
|
||||
void *pointer_value = &dummy_value;
|
||||
assert_singleton_execution2();
|
||||
g_once_init_leave (&initialized, (gsize) pointer_value);
|
||||
}
|
||||
return (void*) initialized;
|
||||
}
|
||||
|
||||
static void
|
||||
initializer3 (void)
|
||||
{
|
||||
static gsize initialized = 0;
|
||||
if (g_once_init_enter (&initialized))
|
||||
{
|
||||
gsize initval = 42;
|
||||
assert_singleton_execution3();
|
||||
g_usleep (25 * 1000); /* waste time for multiple threads to wait */
|
||||
g_once_init_leave (&initialized, initval);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer
|
||||
tmain_call_initializer3 (gpointer user_data)
|
||||
{
|
||||
g_mutex_lock (&tmutex);
|
||||
g_cond_wait (&tcond, &tmutex);
|
||||
g_mutex_unlock (&tmutex);
|
||||
//g_printf ("[");
|
||||
initializer3();
|
||||
//g_printf ("]\n");
|
||||
g_atomic_int_add (&thread_call_count, 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* stress_concurrent_initializers (void*);
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
G_GNUC_UNUSED GThread *threads[N_THREADS];
|
||||
int i;
|
||||
void *p;
|
||||
|
||||
/* test simple initializer */
|
||||
initializer1();
|
||||
initializer1();
|
||||
/* test pointer initializer */
|
||||
p = initializer2();
|
||||
g_assert (p == &dummy_value);
|
||||
p = initializer2();
|
||||
g_assert (p == &dummy_value);
|
||||
/* start multiple threads for initializer3() */
|
||||
g_mutex_lock (&tmutex);
|
||||
for (i = 0; i < N_THREADS; i++)
|
||||
threads[i] = g_thread_create (tmain_call_initializer3, 0, FALSE, NULL);
|
||||
g_mutex_unlock (&tmutex);
|
||||
/* concurrently call initializer3() */
|
||||
g_cond_broadcast (&tcond);
|
||||
/* loop until all threads passed the call to initializer3() */
|
||||
while (g_atomic_int_get (&thread_call_count) < i)
|
||||
{
|
||||
if (rand() % 2)
|
||||
g_thread_yield(); /* concurrent shuffling for single core */
|
||||
else
|
||||
g_usleep (1000); /* concurrent shuffling for multi core */
|
||||
g_cond_broadcast (&tcond);
|
||||
}
|
||||
/* call multiple (unoptimized) initializers from multiple threads */
|
||||
g_mutex_lock (&tmutex);
|
||||
g_atomic_int_set (&thread_call_count, 0);
|
||||
for (i = 0; i < N_THREADS; i++)
|
||||
g_thread_create (stress_concurrent_initializers, 0, FALSE, NULL);
|
||||
g_mutex_unlock (&tmutex);
|
||||
while (g_atomic_int_get (&thread_call_count) < 256 * 4 * N_THREADS)
|
||||
g_usleep (50 * 1000); /* wait for all 5 threads to complete */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get rid of g_once_init_enter-optimizations in the below definitions
|
||||
* to uncover possible races in the g_once_init_enter_impl()/
|
||||
* g_once_init_leave() implementations
|
||||
*/
|
||||
#undef g_once_init_enter
|
||||
#undef g_once_init_leave
|
||||
|
||||
/* define 16 * 16 simple initializers */
|
||||
#define DEFINE_TEST_INITIALIZER(N) \
|
||||
static void \
|
||||
test_initializer_##N (void) \
|
||||
{ \
|
||||
static gsize initialized = 0; \
|
||||
if (g_once_init_enter (&initialized)) \
|
||||
{ \
|
||||
g_free (g_strdup_printf ("cpuhog%5d", 1)); \
|
||||
g_free (g_strdup_printf ("cpuhog%6d", 2)); \
|
||||
g_free (g_strdup_printf ("cpuhog%7d", 3)); \
|
||||
g_once_init_leave (&initialized, 1); \
|
||||
} \
|
||||
}
|
||||
#define DEFINE_16_TEST_INITIALIZERS(P) \
|
||||
DEFINE_TEST_INITIALIZER (P##0) \
|
||||
DEFINE_TEST_INITIALIZER (P##1) \
|
||||
DEFINE_TEST_INITIALIZER (P##2) \
|
||||
DEFINE_TEST_INITIALIZER (P##3) \
|
||||
DEFINE_TEST_INITIALIZER (P##4) \
|
||||
DEFINE_TEST_INITIALIZER (P##5) \
|
||||
DEFINE_TEST_INITIALIZER (P##6) \
|
||||
DEFINE_TEST_INITIALIZER (P##7) \
|
||||
DEFINE_TEST_INITIALIZER (P##8) \
|
||||
DEFINE_TEST_INITIALIZER (P##9) \
|
||||
DEFINE_TEST_INITIALIZER (P##a) \
|
||||
DEFINE_TEST_INITIALIZER (P##b) \
|
||||
DEFINE_TEST_INITIALIZER (P##c) \
|
||||
DEFINE_TEST_INITIALIZER (P##d) \
|
||||
DEFINE_TEST_INITIALIZER (P##e) \
|
||||
DEFINE_TEST_INITIALIZER (P##f)
|
||||
#define DEFINE_256_TEST_INITIALIZERS(P) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_0) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_1) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_2) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_3) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_4) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_5) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_6) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_7) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_8) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_9) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_a) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_b) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_c) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_d) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_e) \
|
||||
DEFINE_16_TEST_INITIALIZERS (P##_f)
|
||||
|
||||
/* list 16 * 16 simple initializers */
|
||||
#define LIST_16_TEST_INITIALIZERS(P) \
|
||||
test_initializer_##P##0, \
|
||||
test_initializer_##P##1, \
|
||||
test_initializer_##P##2, \
|
||||
test_initializer_##P##3, \
|
||||
test_initializer_##P##4, \
|
||||
test_initializer_##P##5, \
|
||||
test_initializer_##P##6, \
|
||||
test_initializer_##P##7, \
|
||||
test_initializer_##P##8, \
|
||||
test_initializer_##P##9, \
|
||||
test_initializer_##P##a, \
|
||||
test_initializer_##P##b, \
|
||||
test_initializer_##P##c, \
|
||||
test_initializer_##P##d, \
|
||||
test_initializer_##P##e, \
|
||||
test_initializer_##P##f
|
||||
#define LIST_256_TEST_INITIALIZERS(P) \
|
||||
LIST_16_TEST_INITIALIZERS (P##_0), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_1), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_2), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_3), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_4), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_5), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_6), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_7), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_8), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_9), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_a), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_b), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_c), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_d), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_e), \
|
||||
LIST_16_TEST_INITIALIZERS (P##_f)
|
||||
|
||||
/* define 4 * 256 initializers */
|
||||
DEFINE_256_TEST_INITIALIZERS (stress1);
|
||||
DEFINE_256_TEST_INITIALIZERS (stress2);
|
||||
DEFINE_256_TEST_INITIALIZERS (stress3);
|
||||
DEFINE_256_TEST_INITIALIZERS (stress4);
|
||||
|
||||
/* call the above 1024 initializers */
|
||||
static void*
|
||||
stress_concurrent_initializers (void *user_data)
|
||||
{
|
||||
static void (*initializers[]) (void) = {
|
||||
LIST_256_TEST_INITIALIZERS (stress1),
|
||||
LIST_256_TEST_INITIALIZERS (stress2),
|
||||
LIST_256_TEST_INITIALIZERS (stress3),
|
||||
LIST_256_TEST_INITIALIZERS (stress4),
|
||||
};
|
||||
gsize i;
|
||||
/* sync to main thread */
|
||||
g_mutex_lock (&tmutex);
|
||||
g_mutex_unlock (&tmutex);
|
||||
/* initialize concurrently */
|
||||
for (i = 0; i < G_N_ELEMENTS (initializers); i++)
|
||||
{
|
||||
initializers[i]();
|
||||
g_atomic_int_add (&thread_call_count, 1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
60
tests/refcount/meson.build
Normal file
60
tests/refcount/meson.build
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
refcount_tests = {
|
||||
'objects' : {},
|
||||
'objects2' : {'suite' : ['slow']},
|
||||
'properties' : {},
|
||||
'properties2' : {'suite' : ['slow']},
|
||||
'properties3' : {'suite' : ['slow']},
|
||||
'properties4' : {},
|
||||
'signal1' : {
|
||||
'source' : 'signals.c',
|
||||
'c_args' : ['-DTESTNUM=1'],
|
||||
},
|
||||
'signal2' : {
|
||||
'source' : 'signals.c',
|
||||
'c_args' : ['-DTESTNUM=2'],
|
||||
},
|
||||
'signal3' : {
|
||||
'source' : 'signals.c',
|
||||
'c_args' : ['-DTESTNUM=3'],
|
||||
},
|
||||
'signal4' : {
|
||||
'source' : 'signals.c',
|
||||
'c_args' : ['-DTESTNUM=4'],
|
||||
},
|
||||
}
|
||||
|
||||
common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
|
||||
common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
|
||||
|
||||
foreach test_name, extra_args : refcount_tests
|
||||
source = extra_args.get('source', test_name + '.c')
|
||||
extra_sources = extra_args.get('extra_sources', [])
|
||||
install = installed_tests_enabled and extra_args.get('install', true)
|
||||
|
||||
if install
|
||||
test_conf = configuration_data()
|
||||
test_conf.set('installed_tests_dir', installed_tests_execdir)
|
||||
test_conf.set('program', test_name)
|
||||
test_conf.set('env', '')
|
||||
configure_file(
|
||||
input: installed_tests_template,
|
||||
output: test_name + '.test',
|
||||
install_dir: installed_tests_metadir,
|
||||
configuration: test_conf
|
||||
)
|
||||
endif
|
||||
|
||||
# FIXME? $(GLIB_DEBUG_FLAGS)
|
||||
exe = executable(test_name, [source, extra_sources],
|
||||
c_args : common_c_args + extra_args.get('c_args', []),
|
||||
dependencies : common_deps + extra_args.get('dependencies', []),
|
||||
install_dir: installed_tests_execdir,
|
||||
install: install,
|
||||
)
|
||||
|
||||
suite = ['refcount'] + extra_args.get('suite', [])
|
||||
timeout = suite.contains('slow') ? test_timeout_slow : test_timeout
|
||||
|
||||
# FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
|
||||
test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
|
||||
endforeach
|
||||
163
tests/refcount/objects.c
Normal file
163
tests/refcount/objects.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
static gint stopping; /* (atomic) */
|
||||
|
||||
static void my_test_class_init (GTestClass * klass);
|
||||
static void my_test_init (GTest * test);
|
||||
static void my_test_dispose (GObject * object);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
|
||||
static GType
|
||||
my_test_get_type (void)
|
||||
{
|
||||
static GType test_type = 0;
|
||||
|
||||
if (!test_type) {
|
||||
const GTypeInfo test_info = {
|
||||
sizeof (GTestClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) my_test_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GTest),
|
||||
0,
|
||||
(GInstanceInitFunc) my_test_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
|
||||
&test_info, 0);
|
||||
}
|
||||
return test_type;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->dispose = my_test_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
g_print ("init %p\n", test);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_dispose (GObject * object)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
g_print ("dispose %p!\n", test);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_refcount (GTest * test)
|
||||
{
|
||||
g_object_ref (test);
|
||||
g_object_unref (test);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
run_thread (GTest * test)
|
||||
{
|
||||
gint i = 1;
|
||||
|
||||
while (!g_atomic_int_get (&stopping)) {
|
||||
my_test_do_refcount (test);
|
||||
if ((i++ % 10000) == 0) {
|
||||
g_print (".");
|
||||
g_thread_yield(); /* force context switch */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
guint i;
|
||||
GTest *test1, *test2;
|
||||
GArray *test_threads;
|
||||
const guint n_threads = 5;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
test1 = g_object_new (G_TYPE_TEST, NULL);
|
||||
test2 = g_object_new (G_TYPE_TEST, NULL);
|
||||
|
||||
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
|
||||
|
||||
g_atomic_int_set (&stopping, 0);
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
|
||||
g_array_append_val (test_threads, thread);
|
||||
|
||||
thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
|
||||
g_array_append_val (test_threads, thread);
|
||||
}
|
||||
g_usleep (5000000);
|
||||
|
||||
g_atomic_int_set (&stopping, 1);
|
||||
|
||||
g_print ("\nstopping\n");
|
||||
|
||||
/* join all threads */
|
||||
for (i = 0; i < 2 * n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_array_index (test_threads, GThread *, i);
|
||||
g_thread_join (thread);
|
||||
}
|
||||
|
||||
g_object_unref (test1);
|
||||
g_object_unref (test2);
|
||||
g_array_unref (test_threads);
|
||||
|
||||
g_print ("stopped\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
121
tests/refcount/objects2.c
Normal file
121
tests/refcount/objects2.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
|
||||
static void my_test_class_init (GTestClass * klass);
|
||||
static void my_test_init (GTest * test);
|
||||
static void my_test_dispose (GObject * object);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
|
||||
static GType
|
||||
my_test_get_type (void)
|
||||
{
|
||||
static GType test_type = 0;
|
||||
|
||||
if (!test_type) {
|
||||
const GTypeInfo test_info = {
|
||||
sizeof (GTestClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) my_test_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GTest),
|
||||
0,
|
||||
(GInstanceInitFunc) my_test_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
|
||||
&test_info, 0);
|
||||
}
|
||||
return test_type;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->dispose = my_test_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
g_print ("init %p\n", test);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_dispose (GObject * object)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
g_print ("dispose %p!\n", test);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_refcount (GTest * test)
|
||||
{
|
||||
static guint i = 1;
|
||||
if (i++ % 100000 == 0)
|
||||
g_print (".");
|
||||
g_object_ref (test);
|
||||
g_object_unref (test);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gint i;
|
||||
GTest *test;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
test = g_object_new (G_TYPE_TEST, NULL);
|
||||
|
||||
for (i=0; i<100000000; i++) {
|
||||
my_test_do_refcount (test);
|
||||
}
|
||||
|
||||
g_object_unref (test);
|
||||
|
||||
g_print ("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
237
tests/refcount/properties.c
Normal file
237
tests/refcount/properties.c
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_DUMMY
|
||||
};
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
gint id;
|
||||
gint dummy;
|
||||
|
||||
gint count;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
static gboolean stopping;
|
||||
|
||||
static void my_test_class_init (GTestClass * klass);
|
||||
static void my_test_init (GTest * test);
|
||||
static void my_test_dispose (GObject * object);
|
||||
static void my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
|
||||
static GType
|
||||
my_test_get_type (void)
|
||||
{
|
||||
static GType test_type = 0;
|
||||
|
||||
if (!test_type) {
|
||||
const GTypeInfo test_info = {
|
||||
sizeof (GTestClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) my_test_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GTest),
|
||||
0,
|
||||
(GInstanceInitFunc) my_test_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest", &test_info, 0);
|
||||
}
|
||||
return test_type;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->dispose = my_test_dispose;
|
||||
gobject_class->get_property = my_test_get_property;
|
||||
gobject_class->set_property = my_test_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DUMMY,
|
||||
g_param_spec_int ("dummy",
|
||||
NULL,
|
||||
NULL,
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
static guint static_id = 1;
|
||||
test->id = static_id++;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_dispose (GObject * object)
|
||||
{
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
g_value_set_int (value, test->dummy);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
test->dummy = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dummy_notify (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
test->count++;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_property (GTest * test)
|
||||
{
|
||||
gint dummy;
|
||||
|
||||
g_object_get (test, "dummy", &dummy, NULL);
|
||||
g_object_set (test, "dummy", dummy + 1, NULL);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
run_thread (GTest * test)
|
||||
{
|
||||
gint i = 1;
|
||||
|
||||
while (!g_atomic_int_get (&stopping)) {
|
||||
my_test_do_property (test);
|
||||
if ((i++ % 10000) == 0)
|
||||
{
|
||||
g_print (".%c", 'a' + test->id);
|
||||
g_thread_yield(); /* force context switch */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
#define N_THREADS 5
|
||||
GThread *test_threads[N_THREADS];
|
||||
GTest *test_objects[N_THREADS];
|
||||
gint i;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
for (i = 0; i < N_THREADS; i++) {
|
||||
GTest *test;
|
||||
|
||||
test = g_object_new (G_TYPE_TEST, NULL);
|
||||
test_objects[i] = test;
|
||||
|
||||
g_assert (test->count == test->dummy);
|
||||
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
|
||||
}
|
||||
|
||||
g_atomic_int_set (&stopping, FALSE);
|
||||
|
||||
for (i = 0; i < N_THREADS; i++)
|
||||
test_threads[i] = g_thread_create ((GThreadFunc) run_thread, test_objects[i], TRUE, NULL);
|
||||
|
||||
g_usleep (3000000);
|
||||
|
||||
g_atomic_int_set (&stopping, TRUE);
|
||||
g_print ("\nstopping\n");
|
||||
|
||||
/* join all threads */
|
||||
for (i = 0; i < N_THREADS; i++)
|
||||
g_thread_join (test_threads[i]);
|
||||
|
||||
g_print ("stopped\n");
|
||||
|
||||
for (i = 0; i < N_THREADS; i++) {
|
||||
GTest *test = test_objects[i];
|
||||
|
||||
g_assert (test->count == test->dummy);
|
||||
g_object_unref (test);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
202
tests/refcount/properties2.c
Normal file
202
tests/refcount/properties2.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_DUMMY
|
||||
};
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
|
||||
gint dummy;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
|
||||
static void my_test_class_init (GTestClass * klass);
|
||||
static void my_test_init (GTest * test);
|
||||
static void my_test_dispose (GObject * object);
|
||||
static void my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
|
||||
static GType
|
||||
my_test_get_type (void)
|
||||
{
|
||||
static GType test_type = 0;
|
||||
|
||||
if (!test_type) {
|
||||
const GTypeInfo test_info = {
|
||||
sizeof (GTestClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) my_test_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GTest),
|
||||
0,
|
||||
(GInstanceInitFunc) my_test_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
|
||||
&test_info, 0);
|
||||
}
|
||||
return test_type;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->dispose = my_test_dispose;
|
||||
gobject_class->get_property = my_test_get_property;
|
||||
gobject_class->set_property = my_test_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DUMMY,
|
||||
g_param_spec_int ("dummy",
|
||||
NULL,
|
||||
NULL,
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
g_print ("init %p\n", test);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_dispose (GObject * object)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
g_print ("dispose %p!\n", test);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
g_value_set_int (value, test->dummy);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
test->dummy = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gint count = 0;
|
||||
|
||||
static void
|
||||
dummy_notify (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
count++;
|
||||
if (count % 10000 == 0)
|
||||
g_print (".");
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_property (GTest * test)
|
||||
{
|
||||
gint dummy;
|
||||
|
||||
g_object_get (test, "dummy", &dummy, NULL);
|
||||
g_object_set (test, "dummy", dummy + 1, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gint i;
|
||||
GTest *test;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
test = g_object_new (G_TYPE_TEST, NULL);
|
||||
|
||||
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
|
||||
|
||||
g_assert (count == test->dummy);
|
||||
|
||||
for (i=0; i<1000000; i++) {
|
||||
my_test_do_property (test);
|
||||
}
|
||||
|
||||
g_assert (count == test->dummy);
|
||||
|
||||
g_object_unref (test);
|
||||
|
||||
return 0;
|
||||
}
|
||||
202
tests/refcount/properties3.c
Normal file
202
tests/refcount/properties3.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_DUMMY
|
||||
};
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
gint id;
|
||||
gint dummy;
|
||||
|
||||
gint count;
|
||||
gint setcount;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
G_DEFINE_TYPE (GTest, my_test, G_TYPE_OBJECT)
|
||||
|
||||
static gint stopping; /* (atomic) */
|
||||
|
||||
static void my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->get_property = my_test_get_property;
|
||||
gobject_class->set_property = my_test_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_DUMMY,
|
||||
g_param_spec_int ("dummy",
|
||||
NULL,
|
||||
NULL,
|
||||
0, G_MAXINT, 0,
|
||||
G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
static guint static_id = 1;
|
||||
test->id = static_id++;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
g_value_set_int (value, g_atomic_int_get (&test->dummy));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DUMMY:
|
||||
g_atomic_int_set (&test->dummy, g_value_get_int (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dummy_notify (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
g_atomic_int_inc (&test->count);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_property (GTest * test)
|
||||
{
|
||||
gint dummy;
|
||||
|
||||
g_atomic_int_inc (&test->setcount);
|
||||
|
||||
g_object_get (test, "dummy", &dummy, NULL);
|
||||
g_object_set (test, "dummy", dummy + 1, NULL);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
run_thread (GTest * test)
|
||||
{
|
||||
gint i = 1;
|
||||
|
||||
while (!g_atomic_int_get (&stopping)) {
|
||||
my_test_do_property (test);
|
||||
if ((i++ % 10000) == 0)
|
||||
{
|
||||
g_print (".%c", 'a' + test->id);
|
||||
g_thread_yield(); /* force context switch */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gint i;
|
||||
GTest *test;
|
||||
GArray *test_threads;
|
||||
const gint n_threads = 5;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
test = g_object_new (G_TYPE_TEST, NULL);
|
||||
|
||||
g_assert (test->count == test->dummy);
|
||||
g_signal_connect (test, "notify::dummy", G_CALLBACK (dummy_notify), NULL);
|
||||
|
||||
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
|
||||
|
||||
g_atomic_int_set (&stopping, 0);
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_thread_create ((GThreadFunc) run_thread, test, TRUE, NULL);
|
||||
g_array_append_val (test_threads, thread);
|
||||
}
|
||||
g_usleep (30000000);
|
||||
|
||||
g_atomic_int_set (&stopping, 1);
|
||||
g_print ("\nstopping\n");
|
||||
|
||||
/* join all threads */
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_array_index (test_threads, GThread *, i);
|
||||
g_thread_join (thread);
|
||||
}
|
||||
|
||||
g_print ("stopped\n");
|
||||
|
||||
g_print ("%d %d\n", test->setcount, test->count);
|
||||
|
||||
g_array_free (test_threads, TRUE);
|
||||
g_object_unref (test);
|
||||
|
||||
return 0;
|
||||
}
|
||||
173
tests/refcount/properties4.c
Normal file
173
tests/refcount/properties4.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#define MY_TYPE_BADGER (my_badger_get_type ())
|
||||
#define MY_BADGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_BADGER, MyBadger))
|
||||
#define MY_IS_BADGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_BADGER))
|
||||
#define MY_BADGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_BADGER, MyBadgerClass))
|
||||
#define MY_IS_BADGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_BADGER))
|
||||
#define MY_BADGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MY_TYPE_BADGER, MyBadgerClass))
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_MAMA
|
||||
};
|
||||
|
||||
typedef struct _MyBadger MyBadger;
|
||||
typedef struct _MyBadgerClass MyBadgerClass;
|
||||
|
||||
struct _MyBadger
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
MyBadger * mama;
|
||||
guint mama_notify_count;
|
||||
};
|
||||
|
||||
struct _MyBadgerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GType my_badger_get_type (void);
|
||||
G_DEFINE_TYPE (MyBadger, my_badger, G_TYPE_OBJECT)
|
||||
|
||||
static void my_badger_dispose (GObject * object);
|
||||
|
||||
static void my_badger_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void my_badger_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void my_badger_mama_notify (GObject *object,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void
|
||||
my_badger_class_init (MyBadgerClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->dispose = my_badger_dispose;
|
||||
|
||||
gobject_class->get_property = my_badger_get_property;
|
||||
gobject_class->set_property = my_badger_set_property;
|
||||
|
||||
g_object_class_install_property (gobject_class,
|
||||
PROP_MAMA,
|
||||
g_param_spec_object ("mama",
|
||||
NULL,
|
||||
NULL,
|
||||
MY_TYPE_BADGER,
|
||||
G_PARAM_READWRITE));
|
||||
}
|
||||
|
||||
static void
|
||||
my_badger_init (MyBadger * self)
|
||||
{
|
||||
g_signal_connect (self, "notify::mama", G_CALLBACK (my_badger_mama_notify),
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
my_badger_dispose (GObject * object)
|
||||
{
|
||||
MyBadger * self;
|
||||
|
||||
self = MY_BADGER (object);
|
||||
|
||||
if (self->mama != NULL)
|
||||
{
|
||||
g_object_unref (self->mama);
|
||||
self->mama = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (my_badger_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_badger_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MyBadger *self;
|
||||
|
||||
self = MY_BADGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MAMA:
|
||||
g_value_set_object (value, self->mama);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_badger_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MyBadger *self;
|
||||
|
||||
self = MY_BADGER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MAMA:
|
||||
if (self->mama)
|
||||
g_object_unref (self->mama);
|
||||
self->mama = g_value_dup_object (value);
|
||||
if (self->mama)
|
||||
g_object_set (self->mama, "mama", NULL, NULL); /* another notify */
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_badger_mama_notify (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MyBadger *self;
|
||||
|
||||
self = MY_BADGER (object);
|
||||
|
||||
self->mama_notify_count++;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
MyBadger * badger1, * badger2;
|
||||
gpointer test;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
badger1 = g_object_new (MY_TYPE_BADGER, NULL);
|
||||
badger2 = g_object_new (MY_TYPE_BADGER, NULL);
|
||||
|
||||
g_object_set (badger1, "mama", badger2, NULL);
|
||||
g_assert_cmpuint (badger1->mama_notify_count, ==, 1);
|
||||
g_assert_cmpuint (badger2->mama_notify_count, ==, 1);
|
||||
g_object_get (badger1, "mama", &test, NULL);
|
||||
g_assert (test == badger2);
|
||||
g_object_unref (test);
|
||||
|
||||
g_object_unref (badger1);
|
||||
g_object_unref (badger2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
308
tests/refcount/signals.c
Normal file
308
tests/refcount/signals.c
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define G_TYPE_TEST (my_test_get_type ())
|
||||
#define MY_TEST(test) (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
|
||||
#define MY_IS_TEST(test) (G_TYPE_CHECK_INSTANCE_TYPE ((test), G_TYPE_TEST))
|
||||
#define MY_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), G_TYPE_TEST, GTestClass))
|
||||
#define MY_IS_TEST_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), G_TYPE_TEST))
|
||||
#define MY_TEST_GET_CLASS(test) (G_TYPE_INSTANCE_GET_CLASS ((test), G_TYPE_TEST, GTestClass))
|
||||
|
||||
typedef struct _GTest GTest;
|
||||
typedef struct _GTestClass GTestClass;
|
||||
|
||||
struct _GTest
|
||||
{
|
||||
GObject object;
|
||||
|
||||
gint value;
|
||||
};
|
||||
|
||||
struct _GTestClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*test_signal1) (GTest * test, gint an_int);
|
||||
void (*test_signal2) (GTest * test, gint an_int);
|
||||
gchar * (*test_signal3) (GTest * test, gint an_int);
|
||||
};
|
||||
|
||||
static GType my_test_get_type (void);
|
||||
static gboolean stopping;
|
||||
|
||||
/* Element signals and args */
|
||||
enum
|
||||
{
|
||||
TEST_SIGNAL1,
|
||||
TEST_SIGNAL2,
|
||||
TEST_SIGNAL3,
|
||||
/* add more above */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0,
|
||||
ARG_TEST_PROP
|
||||
};
|
||||
|
||||
static void my_test_class_init (GTestClass * klass);
|
||||
static void my_test_init (GTest * test);
|
||||
static void my_test_dispose (GObject * object);
|
||||
|
||||
static void signal2_handler (GTest * test, gint anint);
|
||||
static gchar * signal3_handler (GTest * test, gint anint);
|
||||
|
||||
static void my_test_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void my_test_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GObjectClass *parent_class = NULL;
|
||||
|
||||
static guint my_test_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static GType
|
||||
my_test_get_type (void)
|
||||
{
|
||||
static GType test_type = 0;
|
||||
|
||||
if (!test_type) {
|
||||
const GTypeInfo test_info = {
|
||||
sizeof (GTestClass),
|
||||
NULL,
|
||||
NULL,
|
||||
(GClassInitFunc) my_test_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof (GTest),
|
||||
0,
|
||||
(GInstanceInitFunc) my_test_init,
|
||||
NULL
|
||||
};
|
||||
|
||||
test_type = g_type_register_static (G_TYPE_OBJECT, "GTest",
|
||||
&test_info, 0);
|
||||
}
|
||||
return test_type;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_class_init (GTestClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
||||
|
||||
gobject_class->dispose = my_test_dispose;
|
||||
gobject_class->set_property = my_test_set_property;
|
||||
gobject_class->get_property = my_test_get_property;
|
||||
|
||||
my_test_signals[TEST_SIGNAL1] =
|
||||
g_signal_new ("test-signal1", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal1), NULL,
|
||||
NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
|
||||
my_test_signals[TEST_SIGNAL2] =
|
||||
g_signal_new ("test-signal2", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal2), NULL,
|
||||
NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
|
||||
my_test_signals[TEST_SIGNAL3] =
|
||||
g_signal_new ("test-signal3", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GTestClass, test_signal3), NULL,
|
||||
NULL, g_cclosure_marshal_generic, G_TYPE_STRING, 1, G_TYPE_INT);
|
||||
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TEST_PROP,
|
||||
g_param_spec_int ("test-prop", "Test Prop", "Test property",
|
||||
0, 1, 0, G_PARAM_READWRITE));
|
||||
|
||||
klass->test_signal2 = signal2_handler;
|
||||
klass->test_signal3 = signal3_handler;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_init (GTest * test)
|
||||
{
|
||||
g_print ("init %p\n", test);
|
||||
|
||||
test->value = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_dispose (GObject * object)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
g_print ("dispose %p!\n", test);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_TEST_PROP:
|
||||
test->value = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GTest *test;
|
||||
|
||||
test = MY_TEST (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_TEST_PROP:
|
||||
g_value_set_int (value, test->value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_signal1 (GTest * test)
|
||||
{
|
||||
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL1], 0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
signal2_handler (GTest * test, gint anint)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_signal2 (GTest * test)
|
||||
{
|
||||
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL2], 0, 0);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
signal3_handler (GTest * test, gint anint)
|
||||
{
|
||||
return g_strdup ("test");
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_signal3 (GTest * test)
|
||||
{
|
||||
gchar *res;
|
||||
|
||||
g_signal_emit (G_OBJECT (test), my_test_signals[TEST_SIGNAL3], 0, 0, &res);
|
||||
g_assert (res);
|
||||
g_free (res);
|
||||
}
|
||||
|
||||
static void
|
||||
my_test_do_prop (GTest * test)
|
||||
{
|
||||
test->value = g_random_int ();
|
||||
g_object_notify (G_OBJECT (test), "test-prop");
|
||||
}
|
||||
|
||||
static gpointer
|
||||
run_thread (GTest * test)
|
||||
{
|
||||
gint i = 1;
|
||||
|
||||
while (!g_atomic_int_get (&stopping)) {
|
||||
if (TESTNUM == 1)
|
||||
my_test_do_signal1 (test);
|
||||
if (TESTNUM == 2)
|
||||
my_test_do_signal2 (test);
|
||||
if (TESTNUM == 3)
|
||||
my_test_do_prop (test);
|
||||
if (TESTNUM == 4)
|
||||
my_test_do_signal3 (test);
|
||||
if ((i++ % 10000) == 0) {
|
||||
g_print (".");
|
||||
g_thread_yield(); /* force context switch */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
notify (GObject *object, GParamSpec *spec, gpointer user_data)
|
||||
{
|
||||
gint value;
|
||||
|
||||
g_object_get (object, "test-prop", &value, NULL);
|
||||
/*g_print ("+ %d", value);*/
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gint i;
|
||||
GTest *test1, *test2;
|
||||
GArray *test_threads;
|
||||
const gint n_threads = 1;
|
||||
|
||||
g_print ("START: %s\n", argv[0]);
|
||||
g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | g_log_set_always_fatal (G_LOG_FATAL_MASK));
|
||||
|
||||
test1 = g_object_new (G_TYPE_TEST, NULL);
|
||||
test2 = g_object_new (G_TYPE_TEST, NULL);
|
||||
|
||||
g_signal_connect (test1, "notify::test-prop", G_CALLBACK (notify), NULL);
|
||||
g_signal_connect (test1, "test-signal1", G_CALLBACK (notify), NULL);
|
||||
g_signal_connect (test1, "test-signal2", G_CALLBACK (notify), NULL);
|
||||
|
||||
test_threads = g_array_new (FALSE, FALSE, sizeof (GThread *));
|
||||
|
||||
stopping = FALSE;
|
||||
|
||||
for (i = 0; i < n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_thread_create ((GThreadFunc) run_thread, test1, TRUE, NULL);
|
||||
g_array_append_val (test_threads, thread);
|
||||
|
||||
thread = g_thread_create ((GThreadFunc) run_thread, test2, TRUE, NULL);
|
||||
g_array_append_val (test_threads, thread);
|
||||
}
|
||||
g_usleep (5000000);
|
||||
|
||||
g_atomic_int_set (&stopping, TRUE);
|
||||
|
||||
g_print ("\nstopping\n");
|
||||
|
||||
/* join all threads */
|
||||
for (i = 0; i < 2 * n_threads; i++) {
|
||||
GThread *thread;
|
||||
|
||||
thread = g_array_index (test_threads, GThread *, i);
|
||||
g_thread_join (thread);
|
||||
}
|
||||
|
||||
g_print ("stopped\n");
|
||||
|
||||
g_array_free (test_threads, TRUE);
|
||||
g_object_unref (test1);
|
||||
g_object_unref (test2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
49
tests/run-assert-msg-test.sh
Executable file
49
tests/run-assert-msg-test.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#! /bin/sh
|
||||
|
||||
fail ()
|
||||
{
|
||||
echo "Test failed: $*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo_v ()
|
||||
{
|
||||
if [ "$verbose" = "1" ]; then
|
||||
echo "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
error_out=/dev/null
|
||||
if [ "$1" = "-v" ]; then
|
||||
verbose=1
|
||||
error_out=/dev/stderr
|
||||
fi
|
||||
|
||||
if [ -z "$LIBTOOL" ]; then
|
||||
if [ -f ../libtool ]; then
|
||||
LIBTOOL=../libtool
|
||||
else
|
||||
LIBTOOL=libtool
|
||||
fi
|
||||
fi
|
||||
|
||||
echo_v "Running assert-msg-test"
|
||||
OUT=$(./assert-msg-test 2>&1) && fail "assert-msg-test should abort"
|
||||
echo "$OUT" | grep -q '^GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)' || \
|
||||
fail "does not print assertion message"
|
||||
|
||||
if ! type gdb >/dev/null 2>&1; then
|
||||
echo_v "Skipped (no gdb installed)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo_v "Running gdb on assert-msg-test"
|
||||
OUT=$($LIBTOOL --mode=execute gdb --batch -x "${srcdir:-.}/assert-msg-test.gdb" ./assert-msg-test 2> $error_out) || fail "failed to run gdb"
|
||||
|
||||
echo_v "Checking if assert message is in __glib_assert_msg"
|
||||
# shellcheck disable=SC2016
|
||||
if ! echo "$OUT" | grep -q '^$1.*"GLib:ERROR:.*assert-msg-test.c:.*:.*main.*: assertion failed: (42 < 0)"'; then
|
||||
fail "__glib_assert_msg does not have assertion message"
|
||||
fi
|
||||
|
||||
echo_v "All tests passed."
|
||||
303
tests/slice-test.c
Normal file
303
tests/slice-test.c
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/* GLIB sliced memory - fast threaded memory chunk allocator
|
||||
* Copyright (C) 2005 Tim Janik
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <glib.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
|
||||
static guint prime_size = 1021; /* 769; 509 */
|
||||
static gboolean clean_memchunks = FALSE;
|
||||
static guint number_of_blocks = 10000; /* total number of blocks allocated */
|
||||
static guint number_of_repetitions = 10000; /* number of alloc+free repetitions */
|
||||
static gboolean want_corruption = FALSE;
|
||||
|
||||
/* --- old memchunk prototypes (memchunks.c) --- */
|
||||
GMemChunk* old_mem_chunk_new (const gchar *name,
|
||||
gulong atom_size,
|
||||
gulong area_size,
|
||||
gint type);
|
||||
void old_mem_chunk_destroy (GMemChunk *mem_chunk);
|
||||
gpointer old_mem_chunk_alloc (GMemChunk *mem_chunk);
|
||||
gpointer old_mem_chunk_alloc0 (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_free (GMemChunk *mem_chunk,
|
||||
gpointer mem);
|
||||
void old_mem_chunk_clean (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_reset (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_print (GMemChunk *mem_chunk);
|
||||
void old_mem_chunk_info (void);
|
||||
#ifndef G_ALLOC_AND_FREE
|
||||
#define G_ALLOC_AND_FREE 2
|
||||
#endif
|
||||
|
||||
/* --- functions --- */
|
||||
static inline int
|
||||
corruption (void)
|
||||
{
|
||||
if (G_UNLIKELY (want_corruption))
|
||||
{
|
||||
/* corruption per call likelyness is about 1:4000000 */
|
||||
guint32 r = g_random_int() % 8000009;
|
||||
return r == 277 ? +1 : r == 281 ? -1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline gpointer
|
||||
memchunk_alloc (GMemChunk **memchunkp,
|
||||
guint size)
|
||||
{
|
||||
size = MAX (size, 1);
|
||||
if (G_UNLIKELY (!*memchunkp))
|
||||
*memchunkp = old_mem_chunk_new ("", size, 4096, G_ALLOC_AND_FREE);
|
||||
return old_mem_chunk_alloc (*memchunkp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
memchunk_free (GMemChunk *memchunk,
|
||||
gpointer chunk)
|
||||
{
|
||||
old_mem_chunk_free (memchunk, chunk);
|
||||
if (clean_memchunks)
|
||||
old_mem_chunk_clean (memchunk);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
test_memchunk_thread (gpointer data)
|
||||
{
|
||||
GMemChunk **memchunks;
|
||||
guint i, j;
|
||||
guint8 **ps;
|
||||
guint *ss;
|
||||
guint32 rand_accu = 2147483563;
|
||||
/* initialize random numbers */
|
||||
if (data)
|
||||
rand_accu = *(guint32*) data;
|
||||
else
|
||||
{
|
||||
GTimeVal rand_tv;
|
||||
g_get_current_time (&rand_tv);
|
||||
rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
|
||||
}
|
||||
|
||||
/* prepare for memchunk creation */
|
||||
memchunks = g_newa0 (GMemChunk*, prime_size);
|
||||
|
||||
ps = g_new (guint8*, number_of_blocks);
|
||||
ss = g_new (guint, number_of_blocks);
|
||||
/* create number_of_blocks random sizes */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
ss[i] = quick_rand32() % prime_size;
|
||||
/* allocate number_of_blocks blocks */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
|
||||
for (j = 0; j < number_of_repetitions; j++)
|
||||
{
|
||||
/* free number_of_blocks/2 blocks */
|
||||
for (i = 0; i < number_of_blocks; i += 2)
|
||||
memchunk_free (memchunks[ss[i]], ps[i]);
|
||||
/* allocate number_of_blocks/2 blocks with new sizes */
|
||||
for (i = 0; i < number_of_blocks; i += 2)
|
||||
{
|
||||
ss[i] = quick_rand32() % prime_size;
|
||||
ps[i] = memchunk_alloc (&memchunks[ss[i]], ss[i]);
|
||||
}
|
||||
}
|
||||
/* free number_of_blocks blocks */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
memchunk_free (memchunks[ss[i]], ps[i]);
|
||||
/* alloc and free many equally sized chunks in a row */
|
||||
for (i = 0; i < number_of_repetitions; i++)
|
||||
{
|
||||
guint sz = quick_rand32() % prime_size;
|
||||
guint k = number_of_blocks / 100;
|
||||
for (j = 0; j < k; j++)
|
||||
ps[j] = memchunk_alloc (&memchunks[sz], sz);
|
||||
for (j = 0; j < k; j++)
|
||||
memchunk_free (memchunks[sz], ps[j]);
|
||||
}
|
||||
/* cleanout memchunks */
|
||||
for (i = 0; i < prime_size; i++)
|
||||
if (memchunks[i])
|
||||
old_mem_chunk_destroy (memchunks[i]);
|
||||
g_free (ps);
|
||||
g_free (ss);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
test_sliced_mem_thread (gpointer data)
|
||||
{
|
||||
guint32 rand_accu = 2147483563;
|
||||
guint i, j;
|
||||
guint8 **ps;
|
||||
guint *ss;
|
||||
|
||||
/* initialize random numbers */
|
||||
if (data)
|
||||
rand_accu = *(guint32*) data;
|
||||
else
|
||||
{
|
||||
GTimeVal rand_tv;
|
||||
g_get_current_time (&rand_tv);
|
||||
rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
|
||||
}
|
||||
|
||||
ps = g_new (guint8*, number_of_blocks);
|
||||
ss = g_new (guint, number_of_blocks);
|
||||
/* create number_of_blocks random sizes */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
ss[i] = quick_rand32() % prime_size;
|
||||
/* allocate number_of_blocks blocks */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
ps[i] = g_slice_alloc (ss[i] + corruption());
|
||||
for (j = 0; j < number_of_repetitions; j++)
|
||||
{
|
||||
/* free number_of_blocks/2 blocks */
|
||||
for (i = 0; i < number_of_blocks; i += 2)
|
||||
g_slice_free1 (ss[i] + corruption(), ps[i] + corruption());
|
||||
/* allocate number_of_blocks/2 blocks with new sizes */
|
||||
for (i = 0; i < number_of_blocks; i += 2)
|
||||
{
|
||||
ss[i] = quick_rand32() % prime_size;
|
||||
ps[i] = g_slice_alloc (ss[i] + corruption());
|
||||
}
|
||||
}
|
||||
/* free number_of_blocks blocks */
|
||||
for (i = 0; i < number_of_blocks; i++)
|
||||
g_slice_free1 (ss[i] + corruption(), ps[i] + corruption());
|
||||
/* alloc and free many equally sized chunks in a row */
|
||||
for (i = 0; i < number_of_repetitions; i++)
|
||||
{
|
||||
guint sz = quick_rand32() % prime_size;
|
||||
guint k = number_of_blocks / 100;
|
||||
for (j = 0; j < k; j++)
|
||||
ps[j] = g_slice_alloc (sz + corruption());
|
||||
for (j = 0; j < k; j++)
|
||||
g_slice_free1 (sz + corruption(), ps[j] + corruption());
|
||||
}
|
||||
g_free (ps);
|
||||
g_free (ss);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
usage (void)
|
||||
{
|
||||
g_print ("Usage: slice-test [n_threads] [G|S|M|O][f][c][~] [maxblocksize] [seed]\n");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
guint seed32, *seedp = NULL;
|
||||
gboolean ccounters = FALSE, use_memchunks = FALSE;
|
||||
guint n_threads = 1;
|
||||
const gchar *mode = "slab allocator + magazine cache", *emode = " ";
|
||||
if (argc > 1)
|
||||
n_threads = g_ascii_strtoull (argv[1], NULL, 10);
|
||||
if (argc > 2)
|
||||
{
|
||||
guint i, l = strlen (argv[2]);
|
||||
for (i = 0; i < l; i++)
|
||||
switch (argv[2][i])
|
||||
{
|
||||
case 'G': /* GLib mode */
|
||||
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
|
||||
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
|
||||
mode = "slab allocator + magazine cache";
|
||||
break;
|
||||
case 'S': /* slab mode */
|
||||
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
|
||||
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
|
||||
mode = "slab allocator";
|
||||
break;
|
||||
case 'M': /* malloc mode */
|
||||
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
|
||||
mode = "system malloc";
|
||||
break;
|
||||
case 'O': /* old memchunks */
|
||||
use_memchunks = TRUE;
|
||||
mode = "old memchunks";
|
||||
break;
|
||||
case 'f': /* eager freeing */
|
||||
g_slice_set_config (G_SLICE_CONFIG_WORKING_SET_MSECS, 0);
|
||||
clean_memchunks = TRUE;
|
||||
emode = " with eager freeing";
|
||||
break;
|
||||
case 'c': /* print contention counters */
|
||||
ccounters = TRUE;
|
||||
break;
|
||||
case '~':
|
||||
want_corruption = TRUE; /* force occasional corruption */
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (argc > 3)
|
||||
prime_size = g_ascii_strtoull (argv[3], NULL, 10);
|
||||
if (argc > 4)
|
||||
{
|
||||
seed32 = g_ascii_strtoull (argv[4], NULL, 10);
|
||||
seedp = &seed32;
|
||||
}
|
||||
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
|
||||
{
|
||||
gchar strseed[64] = "<random>";
|
||||
GThread **threads;
|
||||
guint i;
|
||||
|
||||
if (seedp)
|
||||
g_snprintf (strseed, 64, "%u", *seedp);
|
||||
g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
|
||||
|
||||
threads = g_alloca (sizeof(GThread*) * n_threads);
|
||||
if (!use_memchunks)
|
||||
for (i = 0; i < n_threads; i++)
|
||||
threads[i] = g_thread_create (test_sliced_mem_thread, seedp, TRUE, NULL);
|
||||
else
|
||||
{
|
||||
for (i = 0; i < n_threads; i++)
|
||||
threads[i] = g_thread_create (test_memchunk_thread, seedp, TRUE, NULL);
|
||||
}
|
||||
for (i = 0; i < n_threads; i++)
|
||||
g_thread_join (threads[i]);
|
||||
|
||||
if (ccounters)
|
||||
{
|
||||
guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES);
|
||||
g_print (" ChunkSize | MagazineSize | Contention\n");
|
||||
for (i = 0; i < n_chunks; i++)
|
||||
{
|
||||
gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n);
|
||||
g_print (" %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT " | %9" G_GINT64_FORMAT "\n", vals[0], vals[2], vals[1]);
|
||||
g_free (vals);
|
||||
}
|
||||
}
|
||||
else
|
||||
g_print ("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
166
tests/slice-threadinit.c
Normal file
166
tests/slice-threadinit.c
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/* slice-threadinit.c - test GSlice across g_thread_init
|
||||
* Copyright (C) 2007 Tim Janik
|
||||
*
|
||||
* This work is provided "as is"; redistribution and modification
|
||||
* in whole or in part, in any medium, physical or electronic is
|
||||
* permitted without restriction.
|
||||
*
|
||||
* This work is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* In no event shall the authors or contributors be liable for any
|
||||
* direct, indirect, incidental, special, exemplary, or consequential
|
||||
* damages (including, but not limited to, procurement of substitute
|
||||
* goods or services; loss of use, data, or profits; or business
|
||||
* interruption) however caused and on any theory of liability, whether
|
||||
* in contract, strict liability, or tort (including negligence or
|
||||
* otherwise) arising in any way out of the use of this software, even
|
||||
* if advised of the possibility of such damage.
|
||||
*/
|
||||
#include <glib.h>
|
||||
|
||||
#define N_PAGES (101) /* number of pages to sample */
|
||||
#define SAMPLE_SIZE (7)
|
||||
#define PAGE_SIZE (128) /* must be <= minimum GSlice alignment block */
|
||||
#define MAGAZINE_PROBES { 97, 265, 347 } /* block sizes hopefully unused by g_thread_init */
|
||||
#define MAX_PROBE_TRIALS (1031) /* must be >= maximum magazine size */
|
||||
|
||||
#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))
|
||||
|
||||
static struct {
|
||||
void *page;
|
||||
void *sample;
|
||||
} pages[N_PAGES] = { { NULL, NULL }, };
|
||||
|
||||
static const guint magazine_probes[] = MAGAZINE_PROBES;
|
||||
#define N_MAGAZINE_PROBES G_N_ELEMENTS (magazine_probes)
|
||||
|
||||
static void
|
||||
release_trash_list (GSList **trash_list,
|
||||
gsize block_size)
|
||||
{
|
||||
while (*trash_list)
|
||||
{
|
||||
g_slice_free1 (block_size, (*trash_list)->data);
|
||||
*trash_list = g_slist_delete_link (*trash_list, *trash_list);
|
||||
}
|
||||
}
|
||||
|
||||
static GSList *free_list = NULL;
|
||||
|
||||
static gboolean
|
||||
allocate_from_known_page (void)
|
||||
{
|
||||
guint i, j, n_trials = N_PAGES * PAGE_SIZE / SAMPLE_SIZE; /* upper bound */
|
||||
for (i = 0; i < n_trials; i++)
|
||||
{
|
||||
void *b = g_slice_alloc (SAMPLE_SIZE);
|
||||
void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE));
|
||||
free_list = g_slist_prepend (free_list, b);
|
||||
/* find page */
|
||||
for (j = 0; j < N_PAGES; j++)
|
||||
if (pages[j].page == p)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
gsize j, n_pages = 0;
|
||||
void *mps[N_MAGAZINE_PROBES];
|
||||
|
||||
/* probe some magazine sizes */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
mps[j] = g_slice_alloc (magazine_probes[j]);
|
||||
/* mps[*] now contains pointers to allocated slices */
|
||||
|
||||
/* allocate blocks from N_PAGES different pages */
|
||||
while (n_pages < N_PAGES)
|
||||
{
|
||||
void *b = g_slice_alloc (SAMPLE_SIZE);
|
||||
void *p = (void*) (PAGE_SIZE * ((gsize) b / PAGE_SIZE));
|
||||
for (j = 0; j < N_PAGES; j++)
|
||||
if (pages[j].page == p)
|
||||
break;
|
||||
if (j < N_PAGES) /* known page */
|
||||
free_list = g_slist_prepend (free_list, b);
|
||||
else /* new page */
|
||||
{
|
||||
j = n_pages++;
|
||||
pages[j].page = p;
|
||||
pages[j].sample = b;
|
||||
}
|
||||
}
|
||||
/* release intermediate allocations */
|
||||
release_trash_list (&free_list, SAMPLE_SIZE);
|
||||
|
||||
/* ensure that we can allocate from known pages */
|
||||
if (!allocate_from_known_page())
|
||||
g_error ("failed to allocate from magazine/page cache (before g_thread_init)");
|
||||
/* release intermediate allocations */
|
||||
release_trash_list (&free_list, SAMPLE_SIZE);
|
||||
|
||||
/* release magazine probes to be retained */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
g_slice_free1 (magazine_probes[j], mps[j]);
|
||||
/* mps[*] now contains pointers to releaed slices */
|
||||
|
||||
/* ensure probes were retained */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
{
|
||||
GSList *trash = NULL;
|
||||
guint k;
|
||||
for (k = 0; k < MAX_PROBE_TRIALS; k++)
|
||||
{
|
||||
void *mem = g_slice_alloc (magazine_probes[j]);
|
||||
if (mem == mps[j])
|
||||
break; /* reallocated previously freed slice */
|
||||
trash = g_slist_prepend (trash, mem);
|
||||
}
|
||||
release_trash_list (&trash, magazine_probes[j]);
|
||||
if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */
|
||||
g_error ("failed to reallocate slice from magazine (before g_thread_init): size=%d", magazine_probes[j]);
|
||||
}
|
||||
/* mps[*] now contains pointers to reallocated slices */
|
||||
|
||||
/* release magazine probes to be retained across g_thread_init */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
g_slice_free1 (magazine_probes[j], mps[j]);
|
||||
/* mps[*] now contains pointers to released slices */
|
||||
|
||||
/* initialize threading (should retain allocator state) */
|
||||
g_thread_init (NULL);
|
||||
|
||||
/* ensure probes were retained */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
{
|
||||
GSList *trash = NULL;
|
||||
guint k;
|
||||
for (k = 0; k < MAX_PROBE_TRIALS; k++)
|
||||
{
|
||||
void *mem = g_slice_alloc (magazine_probes[j]);
|
||||
if (mem == mps[j])
|
||||
break; /* reallocated previously freed slice */
|
||||
trash = g_slist_prepend (trash, mem);
|
||||
}
|
||||
release_trash_list (&trash, magazine_probes[j]);
|
||||
if (k >= MAX_PROBE_TRIALS) /* failed to reallocate slice */
|
||||
g_error ("failed to reallocate slice from magazine (after g_thread_init): size=%d", magazine_probes[j]);
|
||||
}
|
||||
/* mps[*] now contains pointers to reallocated slices */
|
||||
|
||||
/* ensure that we can allocate from known pages */
|
||||
if (!allocate_from_known_page())
|
||||
g_error ("failed to allocate from magazine/page cache (after g_thread_init)");
|
||||
|
||||
/* some cleanups */
|
||||
for (j = 0; j < N_MAGAZINE_PROBES; j++)
|
||||
g_slice_free1 (magazine_probes[j], mps[j]);
|
||||
release_trash_list (&free_list, SAMPLE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
88
tests/spawn-test-win32-gui.c
Normal file
88
tests/spawn-test-win32-gui.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
/* For read() and write() */
|
||||
#include <unistd.h>
|
||||
/* Cygwin does not prototype __argc and __argv in stdlib.h */
|
||||
extern int __argc;
|
||||
extern char** __argv;
|
||||
#endif
|
||||
|
||||
int _stdcall
|
||||
WinMain (struct HINSTANCE__ *hInstance,
|
||||
struct HINSTANCE__ *hPrevInstance,
|
||||
char *lpszCmdLine,
|
||||
int nCmdShow)
|
||||
{
|
||||
if (__argc >= 2 && strcmp (__argv[1], "print_argv0") == 0)
|
||||
{
|
||||
printf ("%s", __argv[0]);
|
||||
}
|
||||
else if (__argc <= 2)
|
||||
{
|
||||
printf ("This is stdout\n");
|
||||
fflush (stdout);
|
||||
|
||||
fprintf (stderr, "This is stderr\n");
|
||||
fflush (stderr);
|
||||
}
|
||||
else if (__argc == 4 && strcmp (__argv[1], "pipes") == 0)
|
||||
{
|
||||
int infd = atoi (__argv[2]);
|
||||
int outfd = atoi (__argv[3]);
|
||||
int k, n;
|
||||
char buf[100] = {0};
|
||||
|
||||
if (infd < 0 || outfd < 0)
|
||||
{
|
||||
printf ("spawn-test-win32-gui: illegal fds on command line %s",
|
||||
lpszCmdLine);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
n = strlen ("Hello there");
|
||||
if (write (outfd, &n, sizeof (n)) == -1 ||
|
||||
write (outfd, "Hello there", n) == -1)
|
||||
{
|
||||
int errsv = errno;
|
||||
printf ("spawn-test-win32-gui: Write error: %s", strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if ((k = read (infd, &n, sizeof (n))) != sizeof (n))
|
||||
{
|
||||
printf ("spawn-test-win32-gui: Got only %d bytes, wanted %d",
|
||||
k, (int)sizeof (n));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("spawn-test-win32-gui: Parent says %d bytes to read", n);
|
||||
|
||||
if ((k = read (infd, buf, n)) != n)
|
||||
{
|
||||
int errsv = errno;
|
||||
if (k == -1)
|
||||
printf ("spawn-test-win32-gui: Read error: %s", strerror (errsv));
|
||||
else
|
||||
printf ("spawn-test-win32-gui: Got only %d bytes", k);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
n = strlen ("See ya");
|
||||
if (write (outfd, &n, sizeof (n)) == -1 ||
|
||||
write (outfd, "See ya", n) == -1)
|
||||
{
|
||||
int errsv = errno;
|
||||
printf ("spawn-test-win32-gui: Write error: %s", strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
352
tests/spawn-test.c
Normal file
352
tests/spawn-test.c
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* GLIB - Library of useful routines for C programming
|
||||
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
||||
* file for a list of people on the GLib Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#define pipe(fds) _pipe(fds, 4096, _O_BINARY)
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
run_tests (const gchar* argv0)
|
||||
{
|
||||
GError *err = NULL;
|
||||
gchar *output = NULL;
|
||||
gchar *erroutput = NULL;
|
||||
gchar *dirname = g_path_get_dirname (argv0);
|
||||
#ifdef G_OS_WIN32
|
||||
int pipedown[2], pipeup[2];
|
||||
gchar **argv = 0;
|
||||
gchar spawn_binary[1000] = {0};
|
||||
gchar full_cmdline[1000] = {0};
|
||||
g_snprintf (spawn_binary, sizeof (spawn_binary), "%s\\spawn-test-win32-gui.exe", dirname);
|
||||
#endif
|
||||
g_free (dirname);
|
||||
|
||||
err = NULL;
|
||||
if (!g_spawn_command_line_sync ("nonexistent_application foo 'bar baz' blah blah",
|
||||
NULL, NULL, NULL,
|
||||
&err))
|
||||
{
|
||||
g_error_free (err);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("no error for sync spawn of nonexistent application");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
err = NULL;
|
||||
if (!g_spawn_command_line_async ("nonexistent_application foo bar baz \"blah blah\"",
|
||||
&err))
|
||||
{
|
||||
g_error_free (err);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_warning ("no error for async spawn of nonexistent application");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
err = NULL;
|
||||
#ifdef G_OS_UNIX
|
||||
if (!g_spawn_command_line_sync ("/bin/sh -c 'echo hello'",
|
||||
&output, NULL, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (output != NULL);
|
||||
|
||||
if (strcmp (output, "hello\n") != 0)
|
||||
{
|
||||
printf ("output was '%s', should have been 'hello'\n",
|
||||
output);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
#endif
|
||||
/* Running sort synchronously, collecting its output. 'sort' command is selected
|
||||
* because it is non-builtin command on both unix and win32 with well-defined stdout behaviour.
|
||||
*/
|
||||
g_file_set_contents ("spawn-test-created-file.txt", "line first\nline 2\nline last\n", -1, &err);
|
||||
g_assert_no_error(err);
|
||||
if (!g_spawn_command_line_sync ("sort spawn-test-created-file.txt",
|
||||
&output, &erroutput, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (output != NULL);
|
||||
g_assert (erroutput != NULL);
|
||||
|
||||
if (strstr (output, "\nline first") == 0)
|
||||
{
|
||||
printf ("output was '%s', should have contained 'line first' in second line\n",
|
||||
output);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
if (erroutput[0] != '\0')
|
||||
{
|
||||
printf ("error output was '%s', should have been empty\n",
|
||||
erroutput);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
g_free (erroutput);
|
||||
erroutput = NULL;
|
||||
g_unlink ("spawn-test-created-file.txt");
|
||||
}
|
||||
|
||||
if (!g_spawn_command_line_sync ("sort non-existing-file.txt",
|
||||
NULL, &erroutput, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (erroutput != NULL);
|
||||
|
||||
if (erroutput[0] == '\0')
|
||||
{
|
||||
printf ("erroutput was empty, expected contain error message about non-existing-file.txt\n");
|
||||
exit (1);
|
||||
}
|
||||
g_free (erroutput);
|
||||
erroutput = NULL;
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
printf ("Running spawn-test-win32-gui in various ways.\n");
|
||||
|
||||
printf ("First asynchronously (without wait).\n");
|
||||
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' 1", spawn_binary);
|
||||
if (!g_spawn_command_line_async (full_cmdline, &err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
printf ("Now synchronously, collecting its output.\n");
|
||||
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' 2", spawn_binary);
|
||||
if (!g_spawn_command_line_sync (full_cmdline,
|
||||
&output, &erroutput, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (output != NULL);
|
||||
g_assert (erroutput != NULL);
|
||||
|
||||
if (strcmp (output, "This is stdout\r\n") != 0)
|
||||
{
|
||||
printf ("output was '%s', should have been 'This is stdout'\n",
|
||||
g_strescape (output, NULL));
|
||||
|
||||
exit (1);
|
||||
}
|
||||
if (strcmp (erroutput, "This is stderr\r\n") != 0)
|
||||
{
|
||||
printf ("error output was '%s', should have been 'This is stderr'\n",
|
||||
g_strescape (erroutput, NULL));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
g_free (erroutput);
|
||||
erroutput = NULL;
|
||||
}
|
||||
|
||||
printf ("Now with G_SPAWN_FILE_AND_ARGV_ZERO.\n");
|
||||
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' this-should-be-argv-zero print_argv0", spawn_binary);
|
||||
if (!g_shell_parse_argv (full_cmdline, NULL, &argv, &err))
|
||||
{
|
||||
fprintf (stderr, "Error parsing command line? %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!g_spawn_sync (NULL, argv, NULL,
|
||||
G_SPAWN_FILE_AND_ARGV_ZERO,
|
||||
NULL, NULL, &output, NULL, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp (output, "this-should-be-argv-zero") != 0)
|
||||
{
|
||||
printf ("output was '%s', should have been 'this-should-be-argv-zero'\n", output);
|
||||
exit (1);
|
||||
}
|
||||
g_free (output);
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
printf ("Now talking to it through pipes.\n");
|
||||
|
||||
if (pipe (pipedown) < 0 ||
|
||||
pipe (pipeup) < 0)
|
||||
{
|
||||
fprintf (stderr, "Could not create pipes\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_snprintf (full_cmdline, sizeof (full_cmdline), "'%s' pipes %d %d", spawn_binary, pipedown[0], pipeup[1]);
|
||||
if (!g_shell_parse_argv (full_cmdline,
|
||||
NULL, &argv,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error parsing command line? %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!g_spawn_async (NULL, argv, NULL,
|
||||
G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
|
||||
G_SPAWN_DO_NOT_REAP_CHILD,
|
||||
NULL, NULL, NULL,
|
||||
&err))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", err->message);
|
||||
g_error_free (err);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int k, n;
|
||||
char buf[100];
|
||||
|
||||
if ((k = read (pipeup[0], &n, sizeof (n))) != sizeof (n))
|
||||
{
|
||||
int errsv = errno;
|
||||
if (k == -1)
|
||||
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
|
||||
else
|
||||
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
|
||||
(int)sizeof (n), k);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if ((k = read (pipeup[0], buf, n)) != n)
|
||||
{
|
||||
int errsv = errno;
|
||||
if (k == -1)
|
||||
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
|
||||
else
|
||||
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
|
||||
n, k);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
n = strlen ("Bye then");
|
||||
if (write (pipedown[1], &n, sizeof (n)) == -1 ||
|
||||
write (pipedown[1], "Bye then", n) == -1)
|
||||
{
|
||||
int errsv = errno;
|
||||
fprintf (stderr, "Write error: %s\n", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if ((k = read (pipeup[0], &n, sizeof (n))) != sizeof (n))
|
||||
{
|
||||
int errsv = errno;
|
||||
if (k == -1)
|
||||
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
|
||||
else
|
||||
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
|
||||
(int)sizeof (n), k);
|
||||
exit (1);
|
||||
}
|
||||
if (n != strlen ("See ya"))
|
||||
{
|
||||
printf ("child wrote %d bytes, expected %d", n, (int) strlen ("See ya"));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if ((k = read (pipeup[0], buf, n)) != n)
|
||||
{
|
||||
int errsv = errno;
|
||||
if (k == -1)
|
||||
fprintf (stderr, "Read error: %s\n", g_strerror (errsv));
|
||||
else
|
||||
fprintf (stderr, "Wanted to read %d bytes, got %d\n",
|
||||
n, k);
|
||||
exit (1);
|
||||
}
|
||||
buf[n] = '\0';
|
||||
if (strcmp (buf, "See ya") != 0)
|
||||
{
|
||||
printf ("output was '%s', should have been 'See ya'\n", buf);
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
run_tests (argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
400
tests/thread-test.c
Normal file
400
tests/thread-test.c
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/* GMutex */
|
||||
|
||||
static GMutex test_g_mutex_mutex;
|
||||
static guint test_g_mutex_int = 0;
|
||||
static gboolean test_g_mutex_thread_ready;
|
||||
G_LOCK_DEFINE_STATIC (test_g_mutex);
|
||||
|
||||
static gpointer
|
||||
test_g_mutex_thread (gpointer data)
|
||||
{
|
||||
g_assert (GPOINTER_TO_INT (data) == 42);
|
||||
g_assert (g_mutex_trylock (&test_g_mutex_mutex) == FALSE);
|
||||
g_assert (G_TRYLOCK (test_g_mutex) == FALSE);
|
||||
test_g_mutex_thread_ready = TRUE;
|
||||
g_mutex_lock (&test_g_mutex_mutex);
|
||||
g_assert (test_g_mutex_int == 42);
|
||||
g_mutex_unlock (&test_g_mutex_mutex);
|
||||
|
||||
return GINT_TO_POINTER (41);
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_mutex (void)
|
||||
{
|
||||
GThread *thread;
|
||||
|
||||
g_assert (g_mutex_trylock (&test_g_mutex_mutex));
|
||||
g_assert (G_TRYLOCK (test_g_mutex));
|
||||
test_g_mutex_thread_ready = FALSE;
|
||||
thread = g_thread_create (test_g_mutex_thread, GINT_TO_POINTER (42),
|
||||
TRUE, NULL);
|
||||
/* This busy wait is only for testing purposes and not an example of
|
||||
* good code!*/
|
||||
while (!test_g_mutex_thread_ready)
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
test_g_mutex_int = 42;
|
||||
G_UNLOCK (test_g_mutex);
|
||||
g_mutex_unlock (&test_g_mutex_mutex);
|
||||
g_assert (GPOINTER_TO_INT (g_thread_join (thread)) == 41);
|
||||
}
|
||||
|
||||
/* GStaticRecMutex */
|
||||
|
||||
static GStaticRecMutex test_g_static_rec_mutex_mutex = G_STATIC_REC_MUTEX_INIT;
|
||||
static guint test_g_static_rec_mutex_int = 0;
|
||||
static gboolean test_g_static_rec_mutex_thread_ready;
|
||||
|
||||
static gpointer
|
||||
test_g_static_rec_mutex_thread (gpointer data)
|
||||
{
|
||||
g_assert (GPOINTER_TO_INT (data) == 42);
|
||||
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex)
|
||||
== FALSE);
|
||||
test_g_static_rec_mutex_thread_ready = TRUE;
|
||||
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
|
||||
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
|
||||
g_assert (test_g_static_rec_mutex_int == 42);
|
||||
test_g_static_rec_mutex_thread_ready = FALSE;
|
||||
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
|
||||
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
|
||||
|
||||
g_thread_exit (GINT_TO_POINTER (43));
|
||||
|
||||
g_assert_not_reached ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_static_rec_mutex (void)
|
||||
{
|
||||
GThread *thread;
|
||||
|
||||
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
|
||||
test_g_static_rec_mutex_thread_ready = FALSE;
|
||||
thread = g_thread_create (test_g_static_rec_mutex_thread,
|
||||
GINT_TO_POINTER (42), TRUE, NULL);
|
||||
/* This busy wait is only for testing purposes and not an example of
|
||||
* good code!*/
|
||||
while (!test_g_static_rec_mutex_thread_ready)
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
|
||||
g_assert (g_static_rec_mutex_trylock (&test_g_static_rec_mutex_mutex));
|
||||
test_g_static_rec_mutex_int = 41;
|
||||
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
|
||||
test_g_static_rec_mutex_int = 42;
|
||||
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
|
||||
|
||||
/* This busy wait is only for testing purposes and not an example of
|
||||
* good code!*/
|
||||
while (test_g_static_rec_mutex_thread_ready)
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
|
||||
g_static_rec_mutex_lock (&test_g_static_rec_mutex_mutex);
|
||||
test_g_static_rec_mutex_int = 0;
|
||||
g_static_rec_mutex_unlock (&test_g_static_rec_mutex_mutex);
|
||||
|
||||
g_assert (GPOINTER_TO_INT (g_thread_join (thread)) == 43);
|
||||
}
|
||||
|
||||
/* GStaticPrivate */
|
||||
|
||||
#define THREADS 10
|
||||
|
||||
static GStaticPrivate test_g_static_private_private1 = G_STATIC_PRIVATE_INIT;
|
||||
static GStaticPrivate test_g_static_private_private2 = G_STATIC_PRIVATE_INIT;
|
||||
static GMutex test_g_static_private_mutex;
|
||||
static guint test_g_static_private_counter = 0;
|
||||
static guint test_g_static_private_ready = 0;
|
||||
|
||||
static gpointer
|
||||
test_g_static_private_constructor (void)
|
||||
{
|
||||
g_mutex_lock (&test_g_static_private_mutex);
|
||||
test_g_static_private_counter++;
|
||||
g_mutex_unlock (&test_g_static_private_mutex);
|
||||
return g_new (guint,1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_static_private_destructor (gpointer data)
|
||||
{
|
||||
g_mutex_lock (&test_g_static_private_mutex);
|
||||
test_g_static_private_counter--;
|
||||
g_mutex_unlock (&test_g_static_private_mutex);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
|
||||
static gpointer
|
||||
test_g_static_private_thread (gpointer data)
|
||||
{
|
||||
guint number = GPOINTER_TO_INT (data);
|
||||
guint i;
|
||||
guint *private1, *private2;
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
number = number * 11 + 1; /* A very simple and bad RNG ;-) */
|
||||
private1 = g_static_private_get (&test_g_static_private_private1);
|
||||
if (!private1 || number % 7 > 3)
|
||||
{
|
||||
private1 = test_g_static_private_constructor ();
|
||||
g_static_private_set (&test_g_static_private_private1, private1,
|
||||
test_g_static_private_destructor);
|
||||
}
|
||||
*private1 = number;
|
||||
private2 = g_static_private_get (&test_g_static_private_private2);
|
||||
if (!private2 || number % 13 > 5)
|
||||
{
|
||||
private2 = test_g_static_private_constructor ();
|
||||
g_static_private_set (&test_g_static_private_private2, private2,
|
||||
test_g_static_private_destructor);
|
||||
}
|
||||
*private2 = number * 2;
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
g_assert (number == *private1);
|
||||
g_assert (number * 2 == *private2);
|
||||
}
|
||||
g_mutex_lock (&test_g_static_private_mutex);
|
||||
test_g_static_private_ready++;
|
||||
g_mutex_unlock (&test_g_static_private_mutex);
|
||||
|
||||
/* Busy wait is not nice but that's just a test */
|
||||
while (test_g_static_private_ready != 0)
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
private2 = g_static_private_get (&test_g_static_private_private2);
|
||||
number = number * 11 + 1; /* A very simple and bad RNG ;-) */
|
||||
if (!private2 || number % 13 > 5)
|
||||
{
|
||||
private2 = test_g_static_private_constructor ();
|
||||
g_static_private_set (&test_g_static_private_private2, private2,
|
||||
test_g_static_private_destructor);
|
||||
}
|
||||
*private2 = number * 2;
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
g_assert (number * 2 == *private2);
|
||||
}
|
||||
|
||||
return GINT_TO_POINTER (GPOINTER_TO_INT (data) * 3);
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_static_private (void)
|
||||
{
|
||||
GThread *threads[THREADS];
|
||||
guint i;
|
||||
|
||||
test_g_static_private_ready = 0;
|
||||
|
||||
for (i = 0; i < THREADS; i++)
|
||||
{
|
||||
threads[i] = g_thread_create (test_g_static_private_thread,
|
||||
GINT_TO_POINTER (i), TRUE, NULL);
|
||||
}
|
||||
|
||||
/* Busy wait is not nice but that's just a test */
|
||||
while (test_g_static_private_ready != THREADS)
|
||||
g_usleep (G_USEC_PER_SEC / 5);
|
||||
|
||||
/* Reuse the static private */
|
||||
g_static_private_free (&test_g_static_private_private2);
|
||||
g_static_private_init (&test_g_static_private_private2);
|
||||
|
||||
test_g_static_private_ready = 0;
|
||||
|
||||
for (i = 0; i < THREADS; i++)
|
||||
g_assert (GPOINTER_TO_UINT (g_thread_join (threads[i])) == i * 3);
|
||||
|
||||
g_assert (test_g_static_private_counter == 0);
|
||||
}
|
||||
|
||||
/* GStaticRWLock */
|
||||
|
||||
/* -1 = writing; >0 = # of readers */
|
||||
static gint test_g_static_rw_lock_state = 0;
|
||||
G_LOCK_DEFINE (test_g_static_rw_lock_state);
|
||||
|
||||
static gboolean test_g_static_rw_lock_run = TRUE;
|
||||
static GStaticRWLock test_g_static_rw_lock_lock = G_STATIC_RW_LOCK_INIT;
|
||||
|
||||
static gpointer
|
||||
test_g_static_rw_lock_thread (gpointer data)
|
||||
{
|
||||
while (test_g_static_rw_lock_run)
|
||||
{
|
||||
if (g_random_double() > .2) /* I'm a reader */
|
||||
{
|
||||
|
||||
if (g_random_double() > .2) /* I'll block */
|
||||
g_static_rw_lock_reader_lock (&test_g_static_rw_lock_lock);
|
||||
else /* I'll only try */
|
||||
if (!g_static_rw_lock_reader_trylock (&test_g_static_rw_lock_lock))
|
||||
continue;
|
||||
G_LOCK (test_g_static_rw_lock_state);
|
||||
g_assert (test_g_static_rw_lock_state >= 0);
|
||||
test_g_static_rw_lock_state++;
|
||||
G_UNLOCK (test_g_static_rw_lock_state);
|
||||
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
|
||||
G_LOCK (test_g_static_rw_lock_state);
|
||||
test_g_static_rw_lock_state--;
|
||||
G_UNLOCK (test_g_static_rw_lock_state);
|
||||
|
||||
g_static_rw_lock_reader_unlock (&test_g_static_rw_lock_lock);
|
||||
}
|
||||
else /* I'm a writer */
|
||||
{
|
||||
|
||||
if (g_random_double() > .2) /* I'll block */
|
||||
g_static_rw_lock_writer_lock (&test_g_static_rw_lock_lock);
|
||||
else /* I'll only try */
|
||||
if (!g_static_rw_lock_writer_trylock (&test_g_static_rw_lock_lock))
|
||||
continue;
|
||||
G_LOCK (test_g_static_rw_lock_state);
|
||||
g_assert (test_g_static_rw_lock_state == 0);
|
||||
test_g_static_rw_lock_state = -1;
|
||||
G_UNLOCK (test_g_static_rw_lock_state);
|
||||
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
|
||||
G_LOCK (test_g_static_rw_lock_state);
|
||||
test_g_static_rw_lock_state = 0;
|
||||
G_UNLOCK (test_g_static_rw_lock_state);
|
||||
|
||||
g_static_rw_lock_writer_unlock (&test_g_static_rw_lock_lock);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_static_rw_lock (void)
|
||||
{
|
||||
GThread *threads[THREADS];
|
||||
guint i;
|
||||
for (i = 0; i < THREADS; i++)
|
||||
{
|
||||
threads[i] = g_thread_create (test_g_static_rw_lock_thread,
|
||||
NULL, TRUE, NULL);
|
||||
}
|
||||
g_usleep (G_USEC_PER_SEC * 5);
|
||||
test_g_static_rw_lock_run = FALSE;
|
||||
for (i = 0; i < THREADS; i++)
|
||||
{
|
||||
g_thread_join (threads[i]);
|
||||
}
|
||||
g_assert (test_g_static_rw_lock_state == 0);
|
||||
}
|
||||
|
||||
#define G_ONCE_SIZE 100
|
||||
#define G_ONCE_THREADS 10
|
||||
|
||||
G_LOCK_DEFINE (test_g_once);
|
||||
static guint test_g_once_guint_array[G_ONCE_SIZE];
|
||||
static GOnce test_g_once_array[G_ONCE_SIZE];
|
||||
|
||||
static gpointer
|
||||
test_g_once_init_func(gpointer arg)
|
||||
{
|
||||
guint *count = arg;
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
(*count)++;
|
||||
g_usleep (g_random_int_range (20,1000));
|
||||
return arg;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
test_g_once_thread (gpointer ignore)
|
||||
{
|
||||
guint i;
|
||||
G_LOCK (test_g_once);
|
||||
/* Don't start before all threads are created */
|
||||
G_UNLOCK (test_g_once);
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
guint pos = g_random_int_range (0, G_ONCE_SIZE);
|
||||
gpointer ret = g_once (test_g_once_array + pos, test_g_once_init_func,
|
||||
test_g_once_guint_array + pos);
|
||||
g_assert (ret == test_g_once_guint_array + pos);
|
||||
}
|
||||
|
||||
/* Make sure, that all counters are touched at least once */
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
gpointer ret = g_once (test_g_once_array + i, test_g_once_init_func,
|
||||
test_g_once_guint_array + i);
|
||||
g_assert (ret == test_g_once_guint_array + i);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_g_thread_once (void)
|
||||
{
|
||||
static GOnce once_init = G_ONCE_INIT;
|
||||
GThread *threads[G_ONCE_THREADS];
|
||||
guint i;
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
test_g_once_array[i] = once_init;
|
||||
test_g_once_guint_array[i] = i;
|
||||
}
|
||||
G_LOCK (test_g_once);
|
||||
for (i = 0; i < G_ONCE_THREADS; i++)
|
||||
{
|
||||
threads[i] = g_thread_create (test_g_once_thread, GUINT_TO_POINTER(i%2),
|
||||
TRUE, NULL);
|
||||
}
|
||||
G_UNLOCK (test_g_once);
|
||||
for (i = 0; i < G_ONCE_THREADS; i++)
|
||||
{
|
||||
g_thread_join (threads[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < G_ONCE_SIZE; i++)
|
||||
{
|
||||
g_assert (test_g_once_guint_array[i] == i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* run all the tests */
|
||||
static void
|
||||
run_all_tests (void)
|
||||
{
|
||||
test_g_mutex ();
|
||||
test_g_static_rec_mutex ();
|
||||
test_g_static_private ();
|
||||
test_g_static_rw_lock ();
|
||||
test_g_thread_once ();
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
run_all_tests ();
|
||||
|
||||
/* Now we rerun all tests, but this time we fool the system into
|
||||
* thinking, that the available thread system is not native, but
|
||||
* userprovided. */
|
||||
|
||||
g_thread_use_default_impl = FALSE;
|
||||
run_all_tests ();
|
||||
|
||||
/* XXX: And this shows how silly the above non-native tests are */
|
||||
g_static_rw_lock_free (&test_g_static_rw_lock_lock);
|
||||
g_static_rec_mutex_free (&test_g_static_rec_mutex_mutex);
|
||||
g_static_private_free (&test_g_static_private_private2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
465
tests/threadpool-test.c
Normal file
465
tests/threadpool-test.c
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n");
|
||||
#else
|
||||
# define DEBUG_MSG(x)
|
||||
#endif
|
||||
|
||||
#define WAIT 5 /* seconds */
|
||||
#define MAX_THREADS 10
|
||||
|
||||
/* if > 0 the test will run continuously (since the test ends when
|
||||
* thread count is 0), if -1 it means no limit to unused threads
|
||||
* if 0 then no unused threads are possible */
|
||||
#define MAX_UNUSED_THREADS -1
|
||||
|
||||
G_LOCK_DEFINE_STATIC (thread_counter_pools);
|
||||
|
||||
static gulong abs_thread_counter = 0;
|
||||
static gulong running_thread_counter = 0;
|
||||
static gulong leftover_task_counter = 0;
|
||||
|
||||
G_LOCK_DEFINE_STATIC (last_thread);
|
||||
|
||||
static guint last_thread_id = 0;
|
||||
|
||||
G_LOCK_DEFINE_STATIC (thread_counter_sort);
|
||||
|
||||
static gulong sort_thread_counter = 0;
|
||||
|
||||
static GThreadPool *idle_pool = NULL;
|
||||
|
||||
static GMainLoop *main_loop = NULL;
|
||||
|
||||
static void
|
||||
test_thread_functions (void)
|
||||
{
|
||||
gint max_unused_threads;
|
||||
guint max_idle_time;
|
||||
|
||||
/* This function attempts to call functions which don't need a
|
||||
* threadpool to operate to make sure no uninitialised pointers
|
||||
* accessed and no errors occur.
|
||||
*/
|
||||
|
||||
max_unused_threads = 3;
|
||||
|
||||
DEBUG_MSG (("[funcs] Setting max unused threads to %d",
|
||||
max_unused_threads));
|
||||
g_thread_pool_set_max_unused_threads (max_unused_threads);
|
||||
|
||||
DEBUG_MSG (("[funcs] Getting max unused threads = %d",
|
||||
g_thread_pool_get_max_unused_threads ()));
|
||||
g_assert (g_thread_pool_get_max_unused_threads() == max_unused_threads);
|
||||
|
||||
DEBUG_MSG (("[funcs] Getting num unused threads = %d",
|
||||
g_thread_pool_get_num_unused_threads ()));
|
||||
g_assert (g_thread_pool_get_num_unused_threads () == 0);
|
||||
|
||||
DEBUG_MSG (("[funcs] Stopping unused threads"));
|
||||
g_thread_pool_stop_unused_threads ();
|
||||
|
||||
max_idle_time = 10 * G_USEC_PER_SEC;
|
||||
|
||||
DEBUG_MSG (("[funcs] Setting max idle time to %d",
|
||||
max_idle_time));
|
||||
g_thread_pool_set_max_idle_time (max_idle_time);
|
||||
|
||||
DEBUG_MSG (("[funcs] Getting max idle time = %d",
|
||||
g_thread_pool_get_max_idle_time ()));
|
||||
g_assert (g_thread_pool_get_max_idle_time () == max_idle_time);
|
||||
|
||||
DEBUG_MSG (("[funcs] Setting max idle time to 0"));
|
||||
g_thread_pool_set_max_idle_time (0);
|
||||
|
||||
DEBUG_MSG (("[funcs] Getting max idle time = %d",
|
||||
g_thread_pool_get_max_idle_time ()));
|
||||
g_assert (g_thread_pool_get_max_idle_time () == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_stop_unused (void)
|
||||
{
|
||||
GThreadPool *pool;
|
||||
guint i;
|
||||
guint limit = 100;
|
||||
|
||||
/* Spawn a few threads. */
|
||||
g_thread_pool_set_max_unused_threads (-1);
|
||||
pool = g_thread_pool_new ((GFunc) g_usleep, NULL, -1, FALSE, NULL);
|
||||
|
||||
for (i = 0; i < limit; i++)
|
||||
g_thread_pool_push (pool, GUINT_TO_POINTER (1000), NULL);
|
||||
|
||||
DEBUG_MSG (("[unused] ===> pushed %d threads onto the idle pool",
|
||||
limit));
|
||||
|
||||
/* Wait for the threads to migrate. */
|
||||
g_usleep (G_USEC_PER_SEC);
|
||||
|
||||
DEBUG_MSG (("[unused] stopping unused threads"));
|
||||
g_thread_pool_stop_unused_threads ();
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if (g_thread_pool_get_num_unused_threads () == 0)
|
||||
break;
|
||||
|
||||
DEBUG_MSG (("[unused] waiting ONE second for threads to die"));
|
||||
|
||||
/* Some time for threads to die. */
|
||||
g_usleep (G_USEC_PER_SEC);
|
||||
}
|
||||
|
||||
DEBUG_MSG (("[unused] stopped idle threads, %d remain",
|
||||
g_thread_pool_get_num_unused_threads ()));
|
||||
|
||||
g_assert (g_thread_pool_get_num_unused_threads () == 0);
|
||||
|
||||
g_thread_pool_set_max_unused_threads (MAX_THREADS);
|
||||
|
||||
DEBUG_MSG (("[unused] cleaning up thread pool"));
|
||||
g_thread_pool_free (pool, FALSE, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_pools_entry_func (gpointer data, gpointer user_data)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
guint id = 0;
|
||||
|
||||
id = GPOINTER_TO_UINT (data);
|
||||
#endif
|
||||
|
||||
DEBUG_MSG (("[pool] ---> [%3.3d] entered thread.", id));
|
||||
|
||||
G_LOCK (thread_counter_pools);
|
||||
abs_thread_counter++;
|
||||
running_thread_counter++;
|
||||
G_UNLOCK (thread_counter_pools);
|
||||
|
||||
g_usleep (g_random_int_range (0, 4000));
|
||||
|
||||
G_LOCK (thread_counter_pools);
|
||||
running_thread_counter--;
|
||||
leftover_task_counter--;
|
||||
|
||||
DEBUG_MSG (("[pool] ---> [%3.3d] exiting thread (abs count:%ld, "
|
||||
"running count:%ld, left over:%ld)",
|
||||
id, abs_thread_counter,
|
||||
running_thread_counter, leftover_task_counter));
|
||||
G_UNLOCK (thread_counter_pools);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_pools (void)
|
||||
{
|
||||
GThreadPool *pool1, *pool2, *pool3;
|
||||
guint runs;
|
||||
guint i;
|
||||
|
||||
pool1 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 3, FALSE, NULL);
|
||||
pool2 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 5, TRUE, NULL);
|
||||
pool3 = g_thread_pool_new ((GFunc)test_thread_pools_entry_func, NULL, 7, TRUE, NULL);
|
||||
|
||||
runs = 300;
|
||||
for (i = 0; i < runs; i++)
|
||||
{
|
||||
g_thread_pool_push (pool1, GUINT_TO_POINTER (i + 1), NULL);
|
||||
g_thread_pool_push (pool2, GUINT_TO_POINTER (i + 1), NULL);
|
||||
g_thread_pool_push (pool3, GUINT_TO_POINTER (i + 1), NULL);
|
||||
|
||||
G_LOCK (thread_counter_pools);
|
||||
leftover_task_counter += 3;
|
||||
G_UNLOCK (thread_counter_pools);
|
||||
}
|
||||
|
||||
g_thread_pool_free (pool1, TRUE, TRUE);
|
||||
g_thread_pool_free (pool2, FALSE, TRUE);
|
||||
g_thread_pool_free (pool3, FALSE, TRUE);
|
||||
|
||||
g_assert (runs * 3 == abs_thread_counter + leftover_task_counter);
|
||||
g_assert (running_thread_counter == 0);
|
||||
}
|
||||
|
||||
static gint
|
||||
test_thread_sort_compare_func (gconstpointer a, gconstpointer b, gpointer user_data)
|
||||
{
|
||||
guint32 id1, id2;
|
||||
|
||||
id1 = GPOINTER_TO_UINT (a);
|
||||
id2 = GPOINTER_TO_UINT (b);
|
||||
|
||||
return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_sort_entry_func (gpointer data, gpointer user_data)
|
||||
{
|
||||
guint thread_id;
|
||||
gboolean is_sorted;
|
||||
|
||||
G_LOCK (last_thread);
|
||||
|
||||
thread_id = GPOINTER_TO_UINT (data);
|
||||
is_sorted = GPOINTER_TO_INT (user_data);
|
||||
|
||||
DEBUG_MSG (("%s ---> entered thread:%2.2d, last thread:%2.2d",
|
||||
is_sorted ? "[ sorted]" : "[unsorted]",
|
||||
thread_id, last_thread_id));
|
||||
|
||||
if (is_sorted) {
|
||||
static gboolean last_failed = FALSE;
|
||||
|
||||
if (last_thread_id > thread_id) {
|
||||
if (last_failed) {
|
||||
g_assert (last_thread_id <= thread_id);
|
||||
}
|
||||
|
||||
/* Here we remember one fail and if it concurrently fails, it
|
||||
* can not be sorted. the last thread id might be < this thread
|
||||
* id if something is added to the queue since threads were
|
||||
* created
|
||||
*/
|
||||
last_failed = TRUE;
|
||||
} else {
|
||||
last_failed = FALSE;
|
||||
}
|
||||
|
||||
last_thread_id = thread_id;
|
||||
}
|
||||
|
||||
G_UNLOCK (last_thread);
|
||||
|
||||
g_usleep (WAIT * 1000);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_sort (gboolean sort)
|
||||
{
|
||||
GThreadPool *pool;
|
||||
guint limit;
|
||||
guint max_threads;
|
||||
guint i;
|
||||
|
||||
limit = MAX_THREADS * 10;
|
||||
|
||||
if (sort) {
|
||||
max_threads = 1;
|
||||
} else {
|
||||
max_threads = MAX_THREADS;
|
||||
}
|
||||
|
||||
/* It is important that we only have a maximum of 1 thread for this
|
||||
* test since the results can not be guaranteed to be sorted if > 1.
|
||||
*
|
||||
* Threads are scheduled by the operating system and are executed at
|
||||
* random. It cannot be assumed that threads are executed in the
|
||||
* order they are created. This was discussed in bug #334943.
|
||||
*/
|
||||
|
||||
pool = g_thread_pool_new (test_thread_sort_entry_func,
|
||||
GINT_TO_POINTER (sort),
|
||||
max_threads,
|
||||
FALSE,
|
||||
NULL);
|
||||
|
||||
g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS);
|
||||
|
||||
if (sort) {
|
||||
g_thread_pool_set_sort_function (pool,
|
||||
test_thread_sort_compare_func,
|
||||
GUINT_TO_POINTER (69));
|
||||
}
|
||||
|
||||
for (i = 0; i < limit; i++) {
|
||||
guint id;
|
||||
|
||||
id = g_random_int_range (1, limit) + 1;
|
||||
g_thread_pool_push (pool, GUINT_TO_POINTER (id), NULL);
|
||||
DEBUG_MSG (("%s ===> pushed new thread with id:%d, number "
|
||||
"of threads:%d, unprocessed:%d",
|
||||
sort ? "[ sorted]" : "[unsorted]",
|
||||
id,
|
||||
g_thread_pool_get_num_threads (pool),
|
||||
g_thread_pool_unprocessed (pool)));
|
||||
}
|
||||
|
||||
g_assert (g_thread_pool_get_max_threads (pool) == (gint) max_threads);
|
||||
g_assert (g_thread_pool_get_num_threads (pool) == (guint) g_thread_pool_get_max_threads (pool));
|
||||
g_thread_pool_free (pool, TRUE, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_idle_time_entry_func (gpointer data, gpointer user_data)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
guint thread_id;
|
||||
|
||||
thread_id = GPOINTER_TO_UINT (data);
|
||||
#endif
|
||||
|
||||
DEBUG_MSG (("[idle] ---> entered thread:%2.2d", thread_id));
|
||||
|
||||
g_usleep (WAIT * 1000);
|
||||
|
||||
DEBUG_MSG (("[idle] <--- exiting thread:%2.2d", thread_id));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_thread_idle_timeout (gpointer data)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
g_thread_pool_push (idle_pool, GUINT_TO_POINTER (100 + i), NULL);
|
||||
DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, number "
|
||||
"of threads:%d, unprocessed:%d",
|
||||
100 + i,
|
||||
g_thread_pool_get_num_threads (idle_pool),
|
||||
g_thread_pool_unprocessed (idle_pool)));
|
||||
}
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_thread_idle_time (void)
|
||||
{
|
||||
guint limit = 50;
|
||||
guint interval = 10000;
|
||||
guint i;
|
||||
|
||||
idle_pool = g_thread_pool_new (test_thread_idle_time_entry_func,
|
||||
NULL,
|
||||
0,
|
||||
FALSE,
|
||||
NULL);
|
||||
|
||||
g_thread_pool_set_max_threads (idle_pool, MAX_THREADS, NULL);
|
||||
g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS);
|
||||
g_thread_pool_set_max_idle_time (interval);
|
||||
|
||||
g_assert (g_thread_pool_get_max_threads (idle_pool) == MAX_THREADS);
|
||||
g_assert (g_thread_pool_get_max_unused_threads () == MAX_UNUSED_THREADS);
|
||||
g_assert (g_thread_pool_get_max_idle_time () == interval);
|
||||
|
||||
for (i = 0; i < limit; i++) {
|
||||
g_thread_pool_push (idle_pool, GUINT_TO_POINTER (i + 1), NULL);
|
||||
DEBUG_MSG (("[idle] ===> pushed new thread with id:%d, "
|
||||
"number of threads:%d, unprocessed:%d",
|
||||
i,
|
||||
g_thread_pool_get_num_threads (idle_pool),
|
||||
g_thread_pool_unprocessed (idle_pool)));
|
||||
}
|
||||
|
||||
g_assert_cmpint (g_thread_pool_unprocessed (idle_pool), <=, limit);
|
||||
|
||||
g_timeout_add ((interval - 1000),
|
||||
test_thread_idle_timeout,
|
||||
GUINT_TO_POINTER (interval));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_check_start_and_stop (gpointer user_data)
|
||||
{
|
||||
static guint test_number = 0;
|
||||
static gboolean run_next = FALSE;
|
||||
gboolean continue_timeout = TRUE;
|
||||
gboolean quit = TRUE;
|
||||
|
||||
if (test_number == 0) {
|
||||
run_next = TRUE;
|
||||
DEBUG_MSG (("***** RUNNING TEST %2.2d *****", test_number));
|
||||
}
|
||||
|
||||
if (run_next) {
|
||||
test_number++;
|
||||
|
||||
switch (test_number) {
|
||||
case 1:
|
||||
test_thread_functions ();
|
||||
break;
|
||||
case 2:
|
||||
test_thread_stop_unused ();
|
||||
break;
|
||||
case 3:
|
||||
test_thread_pools ();
|
||||
break;
|
||||
case 4:
|
||||
test_thread_sort (FALSE);
|
||||
break;
|
||||
case 5:
|
||||
test_thread_sort (TRUE);
|
||||
break;
|
||||
case 6:
|
||||
test_thread_stop_unused ();
|
||||
break;
|
||||
case 7:
|
||||
test_thread_idle_time ();
|
||||
break;
|
||||
default:
|
||||
DEBUG_MSG (("***** END OF TESTS *****"));
|
||||
g_main_loop_quit (main_loop);
|
||||
continue_timeout = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
run_next = FALSE;
|
||||
return continue_timeout;
|
||||
}
|
||||
|
||||
if (test_number == 3) {
|
||||
G_LOCK (thread_counter_pools);
|
||||
quit &= running_thread_counter <= 0;
|
||||
DEBUG_MSG (("***** POOL RUNNING THREAD COUNT:%ld",
|
||||
running_thread_counter));
|
||||
G_UNLOCK (thread_counter_pools);
|
||||
}
|
||||
|
||||
if (test_number == 4 || test_number == 5) {
|
||||
G_LOCK (thread_counter_sort);
|
||||
quit &= sort_thread_counter <= 0;
|
||||
DEBUG_MSG (("***** POOL SORT THREAD COUNT:%ld",
|
||||
sort_thread_counter));
|
||||
G_UNLOCK (thread_counter_sort);
|
||||
}
|
||||
|
||||
if (test_number == 7) {
|
||||
guint idle;
|
||||
|
||||
idle = g_thread_pool_get_num_unused_threads ();
|
||||
quit &= idle < 1;
|
||||
DEBUG_MSG (("***** POOL IDLE THREAD COUNT:%d, UNPROCESSED JOBS:%d",
|
||||
idle, g_thread_pool_unprocessed (idle_pool)));
|
||||
}
|
||||
|
||||
if (quit) {
|
||||
run_next = TRUE;
|
||||
}
|
||||
|
||||
return continue_timeout;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
DEBUG_MSG (("Starting... (in one second)"));
|
||||
g_timeout_add (1000, test_check_start_and_stop, NULL);
|
||||
|
||||
main_loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (main_loop);
|
||||
g_main_loop_unref (main_loop);
|
||||
|
||||
g_thread_pool_free (idle_pool, FALSE, TRUE);
|
||||
return 0;
|
||||
}
|
||||
236
tests/timeloop-basic.c
Normal file
236
tests/timeloop-basic.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
static int n_children = 3;
|
||||
static int n_active_children;
|
||||
static int n_iters = 10000;
|
||||
|
||||
static int write_fds[1024];
|
||||
static struct pollfd poll_fds[1024];
|
||||
|
||||
void
|
||||
my_pipe (int *fds)
|
||||
{
|
||||
if (pipe(fds) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
fprintf (stderr, "Cannot create pipe %s\n", strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
read_all (int fd, char *buf, int len)
|
||||
{
|
||||
size_t bytes_read = 0;
|
||||
gssize count;
|
||||
|
||||
while (bytes_read < len)
|
||||
{
|
||||
count = read (fd, buf + bytes_read, len - bytes_read);
|
||||
if (count < 0)
|
||||
{
|
||||
if (errno != EAGAIN)
|
||||
return FALSE;
|
||||
}
|
||||
else if (count == 0)
|
||||
return FALSE;
|
||||
|
||||
bytes_read += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
write_all (int fd, char *buf, int len)
|
||||
{
|
||||
size_t bytes_written = 0;
|
||||
gssize count;
|
||||
|
||||
while (bytes_written < len)
|
||||
{
|
||||
count = write (fd, buf + bytes_written, len - bytes_written);
|
||||
if (count < 0)
|
||||
{
|
||||
if (errno != EAGAIN)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bytes_written += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
run_child (int in_fd, int out_fd)
|
||||
{
|
||||
int i;
|
||||
int val = 1;
|
||||
|
||||
for (i = 0; i < n_iters; i++)
|
||||
{
|
||||
write_all (out_fd, (char *)&val, sizeof (val));
|
||||
read_all (in_fd, (char *)&val, sizeof (val));
|
||||
}
|
||||
|
||||
val = 0;
|
||||
write_all (out_fd, (char *)&val, sizeof (val));
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
int
|
||||
input_callback (int source, int dest)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (!read_all (source, (char *)&val, sizeof(val)))
|
||||
{
|
||||
fprintf (stderr,"Unexpected EOF\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (val)
|
||||
{
|
||||
write_all (dest, (char *)&val, sizeof(val));
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
close (source);
|
||||
close (dest);
|
||||
|
||||
n_active_children--;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
create_child (int pos)
|
||||
{
|
||||
int pid, errsv;
|
||||
int in_fds[2];
|
||||
int out_fds[2];
|
||||
|
||||
my_pipe (in_fds);
|
||||
my_pipe (out_fds);
|
||||
|
||||
pid = fork ();
|
||||
errsv = errno;
|
||||
|
||||
if (pid > 0) /* Parent */
|
||||
{
|
||||
close (in_fds[0]);
|
||||
close (out_fds[1]);
|
||||
|
||||
write_fds[pos] = in_fds[1];
|
||||
poll_fds[pos].fd = out_fds[0];
|
||||
poll_fds[pos].events = POLLIN;
|
||||
}
|
||||
else if (pid == 0) /* Child */
|
||||
{
|
||||
close (in_fds[1]);
|
||||
close (out_fds[0]);
|
||||
|
||||
setsid ();
|
||||
|
||||
run_child (in_fds[0], out_fds[1]);
|
||||
}
|
||||
else /* Error */
|
||||
{
|
||||
fprintf (stderr,"Cannot fork: %s\n", strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
difftimeval (struct timeval *old, struct timeval *new)
|
||||
{
|
||||
return
|
||||
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i, j;
|
||||
struct rusage old_usage;
|
||||
struct rusage new_usage;
|
||||
|
||||
if (argc > 1)
|
||||
n_children = atoi(argv[1]);
|
||||
|
||||
if (argc > 2)
|
||||
n_iters = atoi(argv[2]);
|
||||
|
||||
printf ("Children: %d Iters: %d\n", n_children, n_iters);
|
||||
|
||||
n_active_children = n_children;
|
||||
for (i = 0; i < n_children; i++)
|
||||
create_child (i);
|
||||
|
||||
getrusage (RUSAGE_SELF, &old_usage);
|
||||
|
||||
while (n_active_children > 0)
|
||||
{
|
||||
int old_n_active_children = n_active_children;
|
||||
|
||||
poll (poll_fds, n_active_children, -1);
|
||||
|
||||
for (i=0; i<n_active_children; i++)
|
||||
{
|
||||
if (poll_fds[i].events & (POLLIN | POLLHUP))
|
||||
{
|
||||
if (!input_callback (poll_fds[i].fd, write_fds[i]))
|
||||
write_fds[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_n_active_children > n_active_children)
|
||||
{
|
||||
j = 0;
|
||||
for (i=0; i<old_n_active_children; i++)
|
||||
{
|
||||
if (write_fds[i] != -1)
|
||||
{
|
||||
if (j < i)
|
||||
{
|
||||
poll_fds[j] = poll_fds[i];
|
||||
write_fds[j] = write_fds[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getrusage (RUSAGE_SELF, &new_usage);
|
||||
|
||||
printf ("Elapsed user: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
|
||||
printf ("Elapsed system: %g\n",
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("Elapsed total: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("total / iteration: %g\n",
|
||||
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
|
||||
(n_iters * n_children));
|
||||
|
||||
return 0;
|
||||
}
|
||||
221
tests/timeloop.c
Normal file
221
tests/timeloop.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
static int n_children = 3;
|
||||
static int n_active_children;
|
||||
static int n_iters = 10000;
|
||||
static GMainLoop *loop;
|
||||
|
||||
static void
|
||||
io_pipe (GIOChannel **channels)
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (pipe(fds) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
fprintf (stderr, "Cannot create pipe %s\n", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
channels[0] = g_io_channel_unix_new (fds[0]);
|
||||
channels[1] = g_io_channel_unix_new (fds[1]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
read_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_read = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_read < len)
|
||||
{
|
||||
err = g_io_channel_read (channel, buf + bytes_read, len - bytes_read, &count);
|
||||
if (err)
|
||||
{
|
||||
if (err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
}
|
||||
else if (count == 0)
|
||||
return FALSE;
|
||||
|
||||
bytes_read += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_all (GIOChannel *channel, char *buf, gsize len)
|
||||
{
|
||||
gsize bytes_written = 0;
|
||||
gsize count;
|
||||
GIOError err;
|
||||
|
||||
while (bytes_written < len)
|
||||
{
|
||||
err = g_io_channel_write (channel, buf + bytes_written, len - bytes_written, &count);
|
||||
if (err && err != G_IO_ERROR_AGAIN)
|
||||
return FALSE;
|
||||
|
||||
bytes_written += count;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
run_child (GIOChannel *in_channel, GIOChannel *out_channel)
|
||||
{
|
||||
int i;
|
||||
int val = 1;
|
||||
GTimer *timer = g_timer_new();
|
||||
|
||||
for (i = 0; i < n_iters; i++)
|
||||
{
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
read_all (in_channel, (char *)&val, sizeof (val));
|
||||
}
|
||||
|
||||
val = 0;
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
|
||||
val = g_timer_elapsed (timer, NULL) * 1000;
|
||||
|
||||
write_all (out_channel, (char *)&val, sizeof (val));
|
||||
g_timer_destroy (timer);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
input_callback (GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int val;
|
||||
GIOChannel *dest = (GIOChannel *)data;
|
||||
|
||||
if (!read_all (source, (char *)&val, sizeof(val)))
|
||||
{
|
||||
fprintf (stderr, "Unexpected EOF\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (val)
|
||||
{
|
||||
write_all (dest, (char *)&val, sizeof(val));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_io_channel_close (source);
|
||||
g_io_channel_close (dest);
|
||||
|
||||
g_io_channel_unref (source);
|
||||
g_io_channel_unref (dest);
|
||||
|
||||
n_active_children--;
|
||||
if (n_active_children == 0)
|
||||
g_main_loop_quit (loop);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_child (void)
|
||||
{
|
||||
int pid, errsv;
|
||||
GIOChannel *in_channels[2];
|
||||
GIOChannel *out_channels[2];
|
||||
|
||||
io_pipe (in_channels);
|
||||
io_pipe (out_channels);
|
||||
|
||||
pid = fork ();
|
||||
errsv = errno;
|
||||
|
||||
if (pid > 0) /* Parent */
|
||||
{
|
||||
g_io_channel_close (in_channels[0]);
|
||||
g_io_channel_unref (in_channels[0]);
|
||||
g_io_channel_close (out_channels[1]);
|
||||
g_io_channel_unref (out_channels[1]);
|
||||
|
||||
g_io_add_watch (out_channels[0], G_IO_IN | G_IO_HUP,
|
||||
input_callback, in_channels[1]);
|
||||
}
|
||||
else if (pid == 0) /* Child */
|
||||
{
|
||||
g_io_channel_close (in_channels[1]);
|
||||
g_io_channel_close (out_channels[0]);
|
||||
|
||||
setsid ();
|
||||
|
||||
run_child (in_channels[0], out_channels[1]);
|
||||
}
|
||||
else /* Error */
|
||||
{
|
||||
fprintf (stderr, "Cannot fork: %s\n", g_strerror (errsv));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
static double
|
||||
difftimeval (struct timeval *old, struct timeval *new)
|
||||
{
|
||||
return
|
||||
(new->tv_sec - old->tv_sec) * 1000. + (new->tv_usec - old->tv_usec) / 1000;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct rusage old_usage;
|
||||
struct rusage new_usage;
|
||||
|
||||
if (argc > 1)
|
||||
n_children = atoi(argv[1]);
|
||||
|
||||
if (argc > 2)
|
||||
n_iters = atoi(argv[2]);
|
||||
|
||||
printf ("Children: %d Iters: %d\n", n_children, n_iters);
|
||||
|
||||
n_active_children = n_children;
|
||||
for (i = 0; i < n_children; i++)
|
||||
create_child ();
|
||||
|
||||
getrusage (RUSAGE_SELF, &old_usage);
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (loop);
|
||||
getrusage (RUSAGE_SELF, &new_usage);
|
||||
|
||||
printf ("Elapsed user: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime));
|
||||
printf ("Elapsed system: %g\n",
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("Elapsed total: %g\n",
|
||||
difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime));
|
||||
printf ("total / iteration: %g\n",
|
||||
(difftimeval (&old_usage.ru_utime, &new_usage.ru_utime) +
|
||||
difftimeval (&old_usage.ru_stime, &new_usage.ru_stime)) /
|
||||
(n_iters * n_children));
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
return 0;
|
||||
}
|
||||
444
tests/unicode-encoding.c
Normal file
444
tests/unicode-encoding.c
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
static gint exit_status = 0;
|
||||
|
||||
G_GNUC_PRINTF (1, 2)
|
||||
static void
|
||||
croak (char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start (va, format);
|
||||
vfprintf (stderr, format, va);
|
||||
va_end (va);
|
||||
|
||||
exit (1);
|
||||
}
|
||||
|
||||
G_GNUC_PRINTF (1, 2)
|
||||
static void
|
||||
fail (char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start (va, format);
|
||||
vfprintf (stderr, format, va);
|
||||
va_end (va);
|
||||
|
||||
exit_status |= 1;
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VALID,
|
||||
INCOMPLETE,
|
||||
NOTUNICODE,
|
||||
OVERLONG,
|
||||
MALFORMED
|
||||
} Status;
|
||||
|
||||
static gboolean
|
||||
ucs4_equal (gunichar *a, gunichar *b)
|
||||
{
|
||||
while (*a && *b && (*a == *b))
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return (*a == *b);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
utf16_equal (gunichar2 *a, gunichar2 *b)
|
||||
{
|
||||
while (*a && *b && (*a == *b))
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return (*a == *b);
|
||||
}
|
||||
|
||||
static gint
|
||||
utf16_count (gunichar2 *a)
|
||||
{
|
||||
gint result = 0;
|
||||
|
||||
while (a[result])
|
||||
result++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
print_ucs4 (const gchar *prefix, gunichar *ucs4, gint ucs4_len)
|
||||
{
|
||||
gint i;
|
||||
g_print ("%s ", prefix);
|
||||
for (i = 0; i < ucs4_len; i++)
|
||||
g_print ("%x ", ucs4[i]);
|
||||
g_print ("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
process (gint line,
|
||||
gchar *utf8,
|
||||
Status status,
|
||||
gunichar *ucs4,
|
||||
gint ucs4_len)
|
||||
{
|
||||
const gchar *end;
|
||||
gboolean is_valid = g_utf8_validate (utf8, -1, &end);
|
||||
GError *error = NULL;
|
||||
glong items_read, items_written;
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case VALID:
|
||||
if (!is_valid)
|
||||
{
|
||||
fail ("line %d: valid but g_utf8_validate returned FALSE\n", line);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NOTUNICODE:
|
||||
case INCOMPLETE:
|
||||
case OVERLONG:
|
||||
case MALFORMED:
|
||||
if (is_valid)
|
||||
{
|
||||
fail ("line %d: invalid but g_utf8_validate returned TRUE\n", line);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == INCOMPLETE)
|
||||
{
|
||||
gunichar *ucs4_result;
|
||||
|
||||
ucs4_result = g_utf8_to_ucs4 (utf8, -1, NULL, NULL, &error);
|
||||
|
||||
if (!error || !g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT))
|
||||
{
|
||||
fail ("line %d: incomplete input not properly detected\n", line);
|
||||
return;
|
||||
}
|
||||
g_clear_error (&error);
|
||||
|
||||
ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, NULL, &error);
|
||||
|
||||
if (!ucs4_result || items_read == (glong) strlen (utf8))
|
||||
{
|
||||
fail ("line %d: incomplete input not properly detected\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free (ucs4_result);
|
||||
}
|
||||
|
||||
if (status == VALID || status == NOTUNICODE)
|
||||
{
|
||||
gunichar *ucs4_result;
|
||||
|
||||
ucs4_result = g_utf8_to_ucs4 (utf8, -1, &items_read, &items_written, &error);
|
||||
if (!ucs4_result)
|
||||
{
|
||||
fail ("line %d: conversion with status %d to ucs4 failed: %s\n", line, status, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ucs4_equal (ucs4_result, ucs4) ||
|
||||
items_read != (glong) strlen (utf8) ||
|
||||
items_written != ucs4_len)
|
||||
{
|
||||
fail ("line %d: results of conversion with status %d to ucs4 do not match expected.\n", line, status);
|
||||
print_ucs4 ("expected: ", ucs4, ucs4_len);
|
||||
print_ucs4 ("received: ", ucs4_result, items_written);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free (ucs4_result);
|
||||
}
|
||||
|
||||
if (status == VALID)
|
||||
{
|
||||
gunichar *ucs4_result;
|
||||
gchar *utf8_result;
|
||||
|
||||
ucs4_result = g_utf8_to_ucs4_fast (utf8, -1, &items_written);
|
||||
|
||||
if (!ucs4_equal (ucs4_result, ucs4) ||
|
||||
items_written != ucs4_len)
|
||||
{
|
||||
fail ("line %d: results of fast conversion with status %d to ucs4 do not match expected.\n", line, status);
|
||||
print_ucs4 ("expected: ", ucs4, ucs4_len);
|
||||
print_ucs4 ("received: ", ucs4_result, items_written);
|
||||
return;
|
||||
}
|
||||
|
||||
utf8_result = g_ucs4_to_utf8 (ucs4_result, -1, &items_read, &items_written, &error);
|
||||
if (!utf8_result)
|
||||
{
|
||||
fail ("line %d: conversion back to utf8 failed: %s", line, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (utf8_result, utf8) != 0 ||
|
||||
items_read != ucs4_len ||
|
||||
items_written != (glong) strlen (utf8))
|
||||
{
|
||||
fail ("line %d: conversion back to utf8 did not match original\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free (utf8_result);
|
||||
g_free (ucs4_result);
|
||||
}
|
||||
|
||||
if (status == VALID)
|
||||
{
|
||||
gunichar2 *utf16_expected_tmp;
|
||||
gunichar2 *utf16_expected;
|
||||
gunichar2 *utf16_from_utf8;
|
||||
gunichar2 *utf16_from_ucs4;
|
||||
gunichar *ucs4_result;
|
||||
gsize bytes_written;
|
||||
gint n_chars;
|
||||
gchar *utf8_result;
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
#define TARGET "UTF-16LE"
|
||||
#else
|
||||
#define TARGET "UTF-16"
|
||||
#endif
|
||||
|
||||
if (!(utf16_expected_tmp = (gunichar2 *)g_convert (utf8, -1, TARGET, "UTF-8",
|
||||
NULL, &bytes_written, NULL)))
|
||||
{
|
||||
fail ("line %d: could not convert to UTF-16 via g_convert\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
/* zero-terminate and remove BOM
|
||||
*/
|
||||
n_chars = bytes_written / 2;
|
||||
if (utf16_expected_tmp[0] == 0xfeff) /* BOM */
|
||||
{
|
||||
n_chars--;
|
||||
utf16_expected = g_new (gunichar2, n_chars + 1);
|
||||
memcpy (utf16_expected, utf16_expected_tmp + 1, sizeof(gunichar2) * n_chars);
|
||||
}
|
||||
else if (utf16_expected_tmp[0] == 0xfffe) /* ANTI-BOM */
|
||||
{
|
||||
fail ("line %d: conversion via iconv to \"UTF-16\" is not native-endian\n", line);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
utf16_expected = g_new (gunichar2, n_chars + 1);
|
||||
memcpy (utf16_expected, utf16_expected_tmp, sizeof(gunichar2) * n_chars);
|
||||
}
|
||||
|
||||
utf16_expected[n_chars] = '\0';
|
||||
|
||||
if (!(utf16_from_utf8 = g_utf8_to_utf16 (utf8, -1, &items_read, &items_written, &error)))
|
||||
{
|
||||
fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (items_read != (glong) strlen (utf8) ||
|
||||
utf16_count (utf16_from_utf8) != items_written)
|
||||
{
|
||||
fail ("line %d: length error in conversion to ucs16\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(utf16_from_ucs4 = g_ucs4_to_utf16 (ucs4, -1, &items_read, &items_written, &error)))
|
||||
{
|
||||
fail ("line %d: conversion to ucs16 failed: %s\n", line, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (items_read != ucs4_len ||
|
||||
utf16_count (utf16_from_ucs4) != items_written)
|
||||
{
|
||||
fail ("line %d: length error in conversion to ucs16\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utf16_equal (utf16_from_utf8, utf16_expected) ||
|
||||
!utf16_equal (utf16_from_ucs4, utf16_expected))
|
||||
{
|
||||
fail ("line %d: results of conversion to ucs16 do not match\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(utf8_result = g_utf16_to_utf8 (utf16_from_utf8, -1, &items_read, &items_written, &error)))
|
||||
{
|
||||
fail ("line %d: conversion back to utf8 failed: %s\n", line, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (items_read != utf16_count (utf16_from_utf8) ||
|
||||
items_written != (glong) strlen (utf8))
|
||||
{
|
||||
fail ("line %d: length error in conversion from ucs16 to utf8\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ucs4_result = g_utf16_to_ucs4 (utf16_from_ucs4, -1, &items_read, &items_written, &error)))
|
||||
{
|
||||
fail ("line %d: conversion back to utf8/ucs4 failed\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (items_read != utf16_count (utf16_from_utf8) ||
|
||||
items_written != ucs4_len)
|
||||
{
|
||||
fail ("line %d: length error in conversion from ucs16 to ucs4\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (utf8, utf8_result) != 0 ||
|
||||
!ucs4_equal (ucs4, ucs4_result))
|
||||
{
|
||||
fail ("line %d: conversion back to utf8/ucs4 did not match original\n", line);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free (utf16_expected_tmp);
|
||||
g_free (utf16_expected);
|
||||
g_free (utf16_from_utf8);
|
||||
g_free (utf16_from_ucs4);
|
||||
g_free (utf8_result);
|
||||
g_free (ucs4_result);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
gchar *testfile;
|
||||
gchar *contents;
|
||||
GError *error = NULL;
|
||||
gchar *p, *end;
|
||||
char *tmp;
|
||||
gint state = 0;
|
||||
gint line = 1;
|
||||
gint start_line = 0; /* Quiet GCC */
|
||||
gchar *utf8 = NULL; /* Quiet GCC */
|
||||
GArray *ucs4;
|
||||
Status status = VALID; /* Quiet GCC */
|
||||
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
testfile = g_test_build_filename (G_TEST_DIST, "utf8.txt", NULL);
|
||||
|
||||
g_file_get_contents (testfile, &contents, NULL, &error);
|
||||
if (error)
|
||||
croak ("Cannot open utf8.txt: %s", error->message);
|
||||
|
||||
ucs4 = g_array_new (TRUE, FALSE, sizeof(gunichar));
|
||||
|
||||
p = contents;
|
||||
|
||||
/* Loop over lines */
|
||||
while (*p)
|
||||
{
|
||||
while (*p && (*p == ' ' || *p == '\t'))
|
||||
p++;
|
||||
|
||||
end = p;
|
||||
while (*end && (*end != '\r' && *end != '\n'))
|
||||
end++;
|
||||
|
||||
if (!*p || *p == '#' || *p == '\r' || *p == '\n')
|
||||
goto next_line;
|
||||
|
||||
tmp = g_strstrip (g_strndup (p, end - p));
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
/* UTF-8 string */
|
||||
start_line = line;
|
||||
utf8 = tmp;
|
||||
tmp = NULL;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Status */
|
||||
if (!strcmp (tmp, "VALID"))
|
||||
status = VALID;
|
||||
else if (!strcmp (tmp, "INCOMPLETE"))
|
||||
status = INCOMPLETE;
|
||||
else if (!strcmp (tmp, "NOTUNICODE"))
|
||||
status = NOTUNICODE;
|
||||
else if (!strcmp (tmp, "OVERLONG"))
|
||||
status = OVERLONG;
|
||||
else if (!strcmp (tmp, "MALFORMED"))
|
||||
status = MALFORMED;
|
||||
else
|
||||
croak ("Invalid status on line %d\n", line);
|
||||
|
||||
if (status != VALID && status != NOTUNICODE)
|
||||
state++; /* No UCS-4 data */
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* UCS-4 version */
|
||||
|
||||
p = strtok (tmp, " \t");
|
||||
while (p)
|
||||
{
|
||||
gchar *endptr;
|
||||
|
||||
gunichar ch = strtoul (p, &endptr, 16);
|
||||
if (*endptr != '\0')
|
||||
croak ("Invalid UCS-4 character on line %d\n", line);
|
||||
|
||||
g_array_append_val (ucs4, ch);
|
||||
|
||||
p = strtok (NULL, " \t");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
g_free (tmp);
|
||||
state = (state + 1) % 3;
|
||||
|
||||
if (state == 0)
|
||||
{
|
||||
process (start_line, utf8, status, (gunichar *)ucs4->data, ucs4->len);
|
||||
g_array_set_size (ucs4, 0);
|
||||
g_free (utf8);
|
||||
}
|
||||
|
||||
next_line:
|
||||
p = end;
|
||||
if (*p && *p == '\r')
|
||||
p++;
|
||||
if (*p && *p == '\n')
|
||||
p++;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
g_free (testfile);
|
||||
g_array_free (ucs4, TRUE);
|
||||
g_free (contents);
|
||||
return exit_status;
|
||||
}
|
||||
210
tests/unicode-normalize.c
Normal file
210
tests/unicode-normalize.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
#undef G_DISABLE_ASSERT
|
||||
#undef G_LOG_DOMAIN
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
gboolean success = TRUE;
|
||||
|
||||
static char *
|
||||
decode (const gchar *input)
|
||||
{
|
||||
unsigned ch;
|
||||
int offset = 0;
|
||||
GString *result = g_string_new (NULL);
|
||||
|
||||
do
|
||||
{
|
||||
if (sscanf (input + offset, "%x", &ch) != 1)
|
||||
{
|
||||
fprintf (stderr, "Error parsing character string %s\n", input);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
g_string_append_unichar (result, ch);
|
||||
|
||||
while (input[offset] && input[offset] != ' ')
|
||||
offset++;
|
||||
while (input[offset] && input[offset] == ' ')
|
||||
offset++;
|
||||
}
|
||||
while (input[offset]);
|
||||
|
||||
return g_string_free (result, FALSE);
|
||||
}
|
||||
|
||||
const char *names[4] = {
|
||||
"NFD",
|
||||
"NFC",
|
||||
"NFKD",
|
||||
"NFKC"
|
||||
};
|
||||
|
||||
static char *
|
||||
encode (const gchar *input)
|
||||
{
|
||||
GString *result = g_string_new(NULL);
|
||||
|
||||
const gchar *p = input;
|
||||
while (*p)
|
||||
{
|
||||
gunichar c = g_utf8_get_char (p);
|
||||
g_string_append_printf (result, "%04X ", c);
|
||||
p = g_utf8_next_char(p);
|
||||
}
|
||||
|
||||
return g_string_free (result, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_form (int line,
|
||||
GNormalizeMode mode,
|
||||
gboolean do_compat,
|
||||
int expected,
|
||||
char **c,
|
||||
char **raw)
|
||||
{
|
||||
int i;
|
||||
|
||||
gboolean mode_is_compat = (mode == G_NORMALIZE_NFKC ||
|
||||
mode == G_NORMALIZE_NFKD);
|
||||
|
||||
if (mode_is_compat || !do_compat)
|
||||
{
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
char *result = g_utf8_normalize (c[i], -1, mode);
|
||||
if (strcmp (result, c[expected]) != 0)
|
||||
{
|
||||
char *result_raw = encode(result);
|
||||
fprintf (stderr, "\nFailure: %d/%d: %s\n", line, i + 1, raw[5]);
|
||||
fprintf (stderr, " g_utf8_normalize (%s, %s) != %s but %s\n",
|
||||
raw[i], names[mode], raw[expected], result_raw);
|
||||
g_free (result_raw);
|
||||
success = FALSE;
|
||||
}
|
||||
|
||||
g_free (result);
|
||||
}
|
||||
}
|
||||
if (mode_is_compat || do_compat)
|
||||
{
|
||||
for (i = 3; i < 5; i++)
|
||||
{
|
||||
char *result = g_utf8_normalize (c[i], -1, mode);
|
||||
if (strcmp (result, c[expected]) != 0)
|
||||
{
|
||||
char *result_raw = encode(result);
|
||||
fprintf (stderr, "\nFailure: %d/%d: %s\n", line, i, raw[5]);
|
||||
fprintf (stderr, " g_utf8_normalize (%s, %s) != %s but %s\n",
|
||||
raw[i], names[mode], raw[expected], result_raw);
|
||||
g_free (result_raw);
|
||||
success = FALSE;
|
||||
}
|
||||
|
||||
g_free (result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
process_one (int line, gchar **columns)
|
||||
{
|
||||
char *c[5];
|
||||
int i;
|
||||
gboolean skip = FALSE;
|
||||
|
||||
for (i=0; i < 5; i++)
|
||||
{
|
||||
c[i] = decode(columns[i]);
|
||||
if (!c[i])
|
||||
skip = TRUE;
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
test_form (line, G_NORMALIZE_NFD, FALSE, 2, c, columns);
|
||||
test_form (line, G_NORMALIZE_NFD, TRUE, 4, c, columns);
|
||||
test_form (line, G_NORMALIZE_NFC, FALSE, 1, c, columns);
|
||||
test_form (line, G_NORMALIZE_NFC, TRUE, 3, c, columns);
|
||||
test_form (line, G_NORMALIZE_NFKD, TRUE, 4, c, columns);
|
||||
test_form (line, G_NORMALIZE_NFKC, TRUE, 3, c, columns);
|
||||
}
|
||||
|
||||
for (i=0; i < 5; i++)
|
||||
g_free (c[i]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
GIOChannel *in;
|
||||
GError *error = NULL;
|
||||
GString *buffer = g_string_new (NULL);
|
||||
int line_to_do = 0;
|
||||
int line = 1;
|
||||
|
||||
if (argc != 2 && argc != 3)
|
||||
{
|
||||
fprintf (stderr, "Usage: unicode-normalize NormalizationTest.txt LINE\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc == 3)
|
||||
line_to_do = atoi(argv[2]);
|
||||
|
||||
in = g_io_channel_new_file (argv[1], "r", &error);
|
||||
if (!in)
|
||||
{
|
||||
fprintf (stderr, "Cannot open %s: %s\n", argv[1], error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
gsize term_pos;
|
||||
gchar **columns;
|
||||
|
||||
if (g_io_channel_read_line_string (in, buffer, &term_pos, &error) != G_IO_STATUS_NORMAL)
|
||||
break;
|
||||
|
||||
if (line_to_do && line != line_to_do)
|
||||
goto next;
|
||||
|
||||
buffer->str[term_pos] = '\0';
|
||||
|
||||
if (buffer->str[0] == '#') /* Comment */
|
||||
goto next;
|
||||
if (buffer->str[0] == '@') /* Part */
|
||||
{
|
||||
fprintf (stderr, "\nProcessing %s\n", buffer->str + 1);
|
||||
goto next;
|
||||
}
|
||||
|
||||
columns = g_strsplit (buffer->str, ";", -1);
|
||||
if (!columns[0])
|
||||
goto next;
|
||||
|
||||
if (!process_one (line, columns))
|
||||
return 1;
|
||||
g_strfreev (columns);
|
||||
|
||||
next:
|
||||
g_string_truncate (buffer, 0);
|
||||
line++;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
fprintf (stderr, "Error reading test file, %s\n", error->message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_io_channel_unref (in);
|
||||
g_string_free (buffer, TRUE);
|
||||
|
||||
return !success;
|
||||
}
|
||||
291
tests/utf8.txt
Normal file
291
tests/utf8.txt
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# This file is derived from
|
||||
#
|
||||
# http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
||||
#
|
||||
# Which was created by Markus Kuhn <mkuhn@acm.org> - 2000-09-02
|
||||
#
|
||||
# lines begining with # and blank lines are ignored
|
||||
#
|
||||
# Beyond that, this file consists of a series of test cases. Each test case consists of
|
||||
# 2 or 3 lines:
|
||||
#
|
||||
# 1. A UTF-8 string
|
||||
# 2. A status
|
||||
# VALID : The string is a valid UTF-8 representation of valid Unicode
|
||||
# INCOMPLETE : The string has a partial character at the end
|
||||
# NOTUNICODE : The string is valid UTF-8, but the characters represented
|
||||
# are not valid unicode (
|
||||
# OVERLONG : The string includes overlong sequences
|
||||
# MALFORMED : The string is not valid UTF-8
|
||||
# 3. If the status is VALID or NOTUNICODE, the UCS-4 representation of the string,
|
||||
# as a series of hex numbers.
|
||||
|
||||
# 1 Some correct UTF-8 text
|
||||
κόσμε
|
||||
VALID
|
||||
03ba 1f79 03c3 03bc 03b5
|
||||
|
||||
# 2.1 First possible sequence of a certain length
|
||||
#
|
||||
# FIXME - handle NULLS?
|
||||
#
|
||||
# [ NULL BYTE ]
|
||||
#VALID
|
||||
#0000
|
||||
|
||||
€
|
||||
VALID
|
||||
0080
|
||||
|
||||
à €
|
||||
VALID
|
||||
0800
|
||||
|
||||
ð<EFBFBD>€€
|
||||
VALID
|
||||
00010000
|
||||
|
||||
øˆ€€€
|
||||
NOTUNICODE
|
||||
00200000
|
||||
|
||||
ü„€€€€
|
||||
NOTUNICODE
|
||||
04000000
|
||||
|
||||
|
||||
VALID
|
||||
0000007f
|
||||
|
||||
ß¿
|
||||
VALID
|
||||
000007ff
|
||||
|
||||
ï¿¿
|
||||
VALID
|
||||
0000ffff
|
||||
|
||||
÷¿¿¿
|
||||
NOTUNICODE
|
||||
001fffff
|
||||
|
||||
û¿¿¿¿
|
||||
NOTUNICODE
|
||||
03ffffff
|
||||
|
||||
ý¿¿¿¿¿
|
||||
NOTUNICODE
|
||||
7fffffff
|
||||
|
||||
# 2.3 Other boundary conditions
|
||||
|
||||
퟿
|
||||
VALID
|
||||
d7ff
|
||||
|
||||

|
||||
VALID
|
||||
e000
|
||||
|
||||
�
|
||||
VALID
|
||||
fffd
|
||||
|
||||
ô<EFBFBD>¿½
|
||||
VALID
|
||||
0010fffd
|
||||
|
||||
ô<EFBFBD>¿¿
|
||||
VALID
|
||||
0010ffff
|
||||
|
||||
ô<EFBFBD>€€
|
||||
NOTUNICODE
|
||||
00110000
|
||||
|
||||
# 3.1 Unexpected continuation bytes
|
||||
|
||||
€
|
||||
MALFORMED
|
||||
¿
|
||||
MALFORMED
|
||||
€¿
|
||||
MALFORMED
|
||||
€¿€
|
||||
MALFORMED
|
||||
€¿€¿
|
||||
MALFORMED
|
||||
€¿€¿€
|
||||
MALFORMED
|
||||
€¿€¿€¿
|
||||
MALFORMED
|
||||
€¿€¿€¿€
|
||||
MALFORMED
|
||||
€<EFBFBD>‚ƒ„…†‡ˆ‰Š‹Œ<EFBFBD>Ž<EFBFBD><EFBFBD>‘’“”•–—˜™š›œ<EFBFBD>žŸ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿
|
||||
MALFORMED
|
||||
|
||||
# 3.2 Lonely start characters
|
||||
|
||||
À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß
|
||||
MALFORMED
|
||||
à á â ã ä å æ ç è é ê ë ì í î ï
|
||||
MALFORMED
|
||||
ð ñ ò ó ô õ ö ÷
|
||||
MALFORMED
|
||||
ø ù ú û
|
||||
MALFORMED
|
||||
ü ý
|
||||
MALFORMED
|
||||
|
||||
# 3.3 Sequences with last continuation byte missing
|
||||
|
||||
À
|
||||
INCOMPLETE
|
||||
à€
|
||||
INCOMPLETE
|
||||
ð€€
|
||||
INCOMPLETE
|
||||
ø€€€
|
||||
INCOMPLETE
|
||||
ü€€€€
|
||||
INCOMPLETE
|
||||
ß
|
||||
INCOMPLETE
|
||||
ï¿
|
||||
INCOMPLETE
|
||||
÷¿¿
|
||||
INCOMPLETE
|
||||
û¿¿¿
|
||||
INCOMPLETE
|
||||
ý¿¿¿¿
|
||||
INCOMPLETE
|
||||
|
||||
# 3.4 Concatenation of incomplete sequences
|
||||
|
||||
Àà€ð€€ø€€€ü€€€€ßï¿÷¿¿û¿¿¿ý¿¿¿¿
|
||||
MALFORMED
|
||||
|
||||
# 3.5 Impossible bytes
|
||||
|
||||
þ
|
||||
MALFORMED
|
||||
ÿ
|
||||
MALFORMED
|
||||
þþÿÿ
|
||||
MALFORMED
|
||||
|
||||
# Examples of an overlong ASCII character
|
||||
|
||||
À¯
|
||||
OVERLONG
|
||||
à€¯
|
||||
OVERLONG
|
||||
ð€€¯
|
||||
OVERLONG
|
||||
ø€€€¯
|
||||
OVERLONG
|
||||
ü€€€€¯
|
||||
OVERLONG
|
||||
|
||||
# Maximum overlong sequences
|
||||
|
||||
Á¿
|
||||
OVERLONG
|
||||
àŸ¿
|
||||
OVERLONG
|
||||
ð<EFBFBD>¿¿
|
||||
OVERLONG
|
||||
ø‡¿¿¿
|
||||
OVERLONG
|
||||
üƒ¿¿¿¿
|
||||
OVERLONG
|
||||
|
||||
# Overlong representation of the NUL character
|
||||
|
||||
À€
|
||||
OVERLONG
|
||||
à€€
|
||||
OVERLONG
|
||||
ð€€€
|
||||
OVERLONG
|
||||
ø€€€€
|
||||
OVERLONG
|
||||
ü€€€€€
|
||||
OVERLONG
|
||||
|
||||
# Illegal code positions
|
||||
|
||||
# Single UTF-16 surrogates
|
||||
|
||||
í €
|
||||
NOTUNICODE
|
||||
d800
|
||||
|
||||
í¿
|
||||
NOTUNICODE
|
||||
db7f
|
||||
|
||||
í®€
|
||||
NOTUNICODE
|
||||
db80
|
||||
|
||||
í¯¿
|
||||
NOTUNICODE
|
||||
dbff
|
||||
|
||||
í°€
|
||||
NOTUNICODE
|
||||
dc00
|
||||
|
||||
í¾€
|
||||
NOTUNICODE
|
||||
df80
|
||||
|
||||
í¿¿
|
||||
NOTUNICODE
|
||||
dfff
|
||||
|
||||
# Paired UTF-16 surrogates
|
||||
|
||||
í €í°€
|
||||
NOTUNICODE
|
||||
d800 dc00
|
||||
|
||||
í €í¿¿
|
||||
NOTUNICODE
|
||||
d800 dfff
|
||||
|
||||
í¿í°€
|
||||
NOTUNICODE
|
||||
db7f dc00
|
||||
|
||||
í¿í¿¿
|
||||
NOTUNICODE
|
||||
db7f dfff
|
||||
|
||||
󰀀
|
||||
NOTUNICODE
|
||||
db80 dc00
|
||||
|
||||
󰏿
|
||||
NOTUNICODE
|
||||
db80 dfff
|
||||
|
||||
􏰀
|
||||
NOTUNICODE
|
||||
dbff dc00
|
||||
|
||||
􏿿
|
||||
NOTUNICODE
|
||||
dbff dfff
|
||||
|
||||
################
|
||||
#
|
||||
# Some more tests, not from Markus Kuhn's file
|
||||
#
|
||||
|
||||
# Mixed plane 0 and higher planes
|
||||
|
||||
Að<EFBFBD>€€Bô<EFBFBD>¿½C
|
||||
VALID
|
||||
41 00010000 42 10fffd 43
|
||||
Loading…
Add table
Add a link
Reference in a new issue