|
|
|
Alex Shulgin
|
Hi all,
Is System.Drawing by any means thread-safe? Can I create some threads, create a Graphics object in each of them and then work with it from within that thread? Is this supposed to work or am I doing something really stupid here? I ask because I've noticed random crashes in a WinForms app on OS X (but it happens on Linux too). Most of the time it crashes in System.Drawing.Graphics' DrawString or MeasureString methods and seems to occur then a background worker thread is working in parallel with UI thread. I've tried to do a stress-test of System.Drawing in a sample multi-threaded program. See attached `test-multi-threaded-drawing.cs'. On my Linux box it crashes all the time. I get a wide variety of errors from gdb stacktraces with SIGSEGV or SIGABRT in the end, to SIGILL with .Net stack trace. Uncommenting these lock {} lines in the ThreadProc helps, but not an option for my real app, as there's simply no single place a lock could be added. I've also tried writing some code in plain C which links to libgdiplus directly: see `threads-gdiplus.c'. It happily crashes just like the C# version. My tests show that even using unsynchronized GdipGetImageGraphicsContext / GdipDeleteGraphics (no fonts stuff touched) can easily lead to crashes. From what I've seen, cairo seems to be thread-safe: see attached[1] `cairo-multi-thread-text.c'. Also, there's a few locking used around thread-unsafe fontconfig calls in libgdiplus itself. I didn't examined the whole code, so there's possibly other places in it missing locking primitives. I would appreciate any help on this subject! -- Regards, Alex [1] originally found in the cairo bugzilla for a few-years-old bug; my sligthly enhanced version /* gmcs test-multi-threaded-drawing.cs -r:System.Drawing,System.Windows.Forms */ using System; using System.Text; using System.Drawing; using System.Windows.Forms; using System.Threading; namespace test { public class MainForm : Form { private static int threadCount = 0; private object consoleLock = new Object(); private object hwndLock = new Object(); public static void Main(string[] args) { threadCount = args.Length == 0 ? 6 : int.Parse(args[0]); Application.Run(new MainForm()); } protected override void OnLoad(EventArgs e) { base.OnLoad(e); for (int i = 0; i < threadCount; ++i) { Thread t = new Thread(ThreadProc); t.Start(i); } } void ThreadProc(object data) { int threadnum = (int) data; Random rnd = new Random((int) DateTime.Now.Ticks); int count = rnd.Next(250, 1000); lock (consoleLock) { Console.WriteLine("thread{0} start: {1}", threadnum, count); } for (int i = 0; i < count; ++i) { string str = CreateRandomString(rnd); using (Graphics g = GetGraphicsForMeasurement()) { using (Font font = CreateRandomFont(rnd)) { //lock (hwndLock) { SizeF sz = g.MeasureString(str, font); using (Bitmap bmp = new Bitmap((int) sz.Width, (int) sz.Height)) { using (Graphics gfx = Graphics.FromImage(bmp)) { using (Brush b = Brushes.Red) { gfx.DrawString(str, font, b, new PointF(0f, 0f)); } } } //} } } } lock (consoleLock) { Console.WriteLine("thread{0} done", threadnum); } } string CreateRandomString(Random rnd) { int len = 1 + rnd.Next(60); StringBuilder sb = new StringBuilder(len); for (int j = 0; j < len; ++j) { int ch = (rnd.Next() & 1) == 1 ? 0x41 : 0x61; // 'A' or 'a' sb.Append(Char.ConvertFromUtf32(ch + rnd.Next(26))); } return sb.ToString(); } Graphics GetGraphicsForMeasurement() { #if NO_GRAPHICS_FROM_HWND Bitmap tmp = new Bitmap(1, 1); return Graphics.FromImage(tmp); #else Graphics g; lock (hwndLock) { g = Graphics.FromHwnd(this.Handle); } return g; #endif } Font CreateRandomFont(Random rnd) { return new Font("Sans", (int) (8 + rnd.NextDouble()*10)); } } } /* gcc `pkg-config --cflags --libs cairo` -lpthread multi-thread-text.c -o multi-thread-text */ /* * Copyright © 2005 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * Red Hat, Inc. not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Red Hat, Inc. makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Carl D. Worth <[hidden email]> */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <cairo.h> #include <pthread.h> static void * start (void *closure) { int i; for (i = 0; i < 1000; ++i) { cairo_surface_t *surface; cairo_t *cr; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100, 100); cr = cairo_create (surface); cairo_move_to (cr, 10, 10); cairo_set_font_size (cr, 10); cairo_show_text (cr, "Hello world.\n"); cairo_set_font_size (cr, 9); cairo_show_text (cr, "Hello world.\n"); cairo_set_font_size (cr, 8); cairo_show_text (cr, "Hello world.\n"); cairo_destroy (cr); cairo_surface_destroy (surface); } return NULL; } int main (int argc, char *argv[0]) { int i, num_threads; pthread_t *pthread; if (argc > 1) { num_threads = atoi (argv[1]); } else { num_threads = 6; printf ("Running with default value of %d threads.\n" "To change, call: %s <number_of_threads>\n", num_threads, argv[0]); } pthread = malloc (num_threads * sizeof (pthread_t)); assert (pthread != NULL); for (i = 0; i < num_threads; i++) pthread_create (&pthread[i], NULL, start, NULL); for (i = 0; i < num_threads; i++) { pthread_join (pthread[i], NULL); printf("joined thread%d\n", i); } return 0; } /* gcc threads-gdiplus.c `pkg-config --cflags glib-2.0` -Wall -lgdiplus -lpthread */ #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wchar.h> #include <gdiplus/GdiPlusFlat.h> #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void fatal(char const* func, GpStatus err) { fprintf(stderr, "%s: %d\n", func, err); exit(1); } static void* start(void* closure) { GpStatus err; GpBitmap* img; int width = 128; int height = 32; int stride = width*4; GpGraphics* gfx; GpFontFamily* family; GpFont* font; WCHAR const str[] = {'H','e','l','l','o',',',' ','W','o','r','l','d','!'}; size_t len = sizeof(str)/sizeof(WCHAR); RectF rect; RectF bounds; int i; for (i = 0; i < 1000; ++i) { err = GdipCreateBitmapFromScan0(width, height, stride, PixelFormat32bppARGB, /* scan0 = */ NULL, &img); if (err != Ok) fatal("GdipCreateBitmapFromScan0", err); //assert(pthread_mutex_lock(&mutex) == 0); err = GdipGetImageGraphicsContext(img, &gfx); if (err != Ok) fatal("GdipGetImageGraphicsContext", err); //assert(pthread_mutex_unlock(&mutex) == 0); err = GdipGetGenericFontFamilySansSerif(&family); if (err != Ok) fatal("GdipGetGenericFontFamilySansSerif", err); err = GdipCreateFont(family, 12.0, FontStyleRegular, UnitPoint, &font); if (err != Ok) fatal("GdipCreateFont", err); rect.X = 0; rect.Y = 0; rect.Width = width; rect.Height = height; //assert(pthread_mutex_lock(&mutex) == 0); err = GdipMeasureString(gfx, str, len, font, &rect, /* format = */ NULL, &bounds, /* codepoints = */ NULL, /* lines = */ NULL); if (err != Ok) fatal("GdipMeasureString", err); //assert(pthread_mutex_unlock(&mutex) == 0); err = GdipDeleteFont(font); if (err != Ok) fatal("GdipDeleteFont", err); err = GdipDeleteGraphics(gfx); if (err != Ok) fatal("GdipDeleteGraphics", err); err = GdipDisposeImage(img); if (err != Ok) fatal("GdipDisposeImage", err); } return NULL; } int main(int argc, char *argv[0]) { int i, num_threads; pthread_t* pthread; GpStatus err; ULONG_PTR gdiptok; GdiplusStartupInput gdipinput; GdiplusStartupOutput gdipoutput; err = GdiplusStartup(&gdiptok, &gdipinput, &gdipoutput); if (err != Ok) fatal("GdiplusStartup", err); if (argc > 1) { num_threads = atoi(argv[1]); } else { num_threads = 6; printf("Running with default value of %d threads.\n" "To change, call: %s <number_of_threads>\n", num_threads, argv[0]); } pthread = malloc(num_threads * sizeof(pthread_t)); assert(pthread != NULL); for (i = 0; i < num_threads; i++) pthread_create(&pthread[i], NULL, start, NULL); for (i = 0; i < num_threads; i++) { pthread_join(pthread[i], NULL); printf("joined thread%d\n", i); } pthread_mutex_destroy(&mutex); return 0; } _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Alex Shulgin
|
Alex Shulgin wrote:
> Hi all, > > Is System.Drawing by any means thread-safe? > > Can I create some threads, create a Graphics object in each of them and > then work with it from within that thread? Is this supposed to work or > am I doing something really stupid here? Oh, forgot to mention that I've tested this on both 2.4 and latest available 2.6. The results are the same. -- Alex _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Rafael Teixeira
|
AFAIK, most classes/methods in System.Drawing (as well as most of the System.* namespaces) are marked as non-threadsafe.
See msdn docs for each class/method to be sure, as we follow suit. Rafael "Monoman" Teixeira --------------------------------------- "To be creative means to be in love with life. You can be creative only if you love life enough that you want to enhance its beauty, you want to bring a little more music to it, a little more poetry to it, a little more dance to it." Osho On Wed, Oct 28, 2009 at 3:47 PM, Alex Shulgin <[hidden email]> wrote:
_______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Sebastien Pouliot
|
In reply to this post
by Alex Shulgin
On Wed, 2009-10-28 at 19:37 +0200, Alex Shulgin wrote:
> Hi all, > > Is System.Drawing by any means thread-safe? No, like most of the .net framework, i.e. <quote>Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.</quote> > > Can I create some threads, create a Graphics object in each of them and > then work with it from within that thread? Yes, you can (or it's a bug). However you also need to make sure you're not using the Graphic instance (and other created objects) only from a single (and original) thread. > Is this supposed to work or > am I doing something really stupid here? > > I ask because I've noticed random crashes in a WinForms app on OS X (but > it happens on Linux too). Most of the time it crashes in > System.Drawing.Graphics' DrawString or MeasureString methods and seems > to occur then a background worker thread is working in parallel with UI > thread. > > I've tried to do a stress-test of System.Drawing in a sample > multi-threaded program. See attached `test-multi-threaded-drawing.cs'. Please open a bug report on bugzilla.novell.com and attach your test case. > On my Linux box it crashes all the time. I get a wide variety of errors > from gdb stacktraces with SIGSEGV or SIGABRT in the end, to SIGILL with > .Net stack trace. > > Uncommenting these lock {} lines in the ThreadProc helps, but not an > option for my real app, as there's simply no single place a lock could > be added. > > I've also tried writing some code in plain C which links to libgdiplus > directly: see `threads-gdiplus.c'. It happily crashes just like the C# > version. Most of System.Drawing is only a small wrapper abound libgdiplus (or GDI + under Windows). Any bug will likely be inside libgdiplus so it's "normal" (i.e. expected) that you get the same behavior. > My tests show that even using unsynchronized GdipGetImageGraphicsContext > / GdipDeleteGraphics (no fonts stuff touched) can easily lead to crashes. > > From what I've seen, cairo seems to be thread-safe: see attached[1] > `cairo-multi-thread-text.c'. It's thread-safe as long as everything is created and executed in the same thread. Otherwise it would not be safe-thread (just like .net and most libraries are). > Also, there's a few locking used around thread-unsafe fontconfig calls > in libgdiplus itself. I didn't examined the whole code, so there's > possibly other places in it missing locking primitives. Yep. Font-related code (and older, pre-1.4, versions of Cairo) had quite a few problems (solved with the locking code). Other than that SWF and ASP.NET were the big clients for SD/libgdiplus and, for SWF, multithreading was not an issue at all. > I would appreciate any help on this subject! > > -- > Regards, > Alex > [1] originally found in the cairo bugzilla for a few-years-old bug; my > sligthly enhanced version > _______________________________________________ > Mono-winforms-list maillist - [hidden email] > http://lists.ximian.com/mailman/listinfo/mono-winforms-list _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Sebastien Pouliot
|
In reply to this post
by Alex Shulgin
On Wed, 2009-10-28 at 19:47 +0200, Alex Shulgin wrote:
> Alex Shulgin wrote: > > Hi all, > > > > Is System.Drawing by any means thread-safe? > > > > Can I create some threads, create a Graphics object in each of them and > > then work with it from within that thread? Is this supposed to work or > > am I doing something really stupid here? > > Oh, forgot to mention that I've tested this on both 2.4 and latest > available 2.6. The results are the same. There has not been many changes between 2.4 and 2.6 wrt System.Drawing.dll and libgdiplus (i.e. minor maintenance). Most graphic stuff (and time) now happens in Moonlight... Sebastien _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Miguel de Icaza
|
In reply to this post
by Alex Shulgin
Hello,
It is thread safe for instances of objects, but you can not mix objects that need to interact with the windowing system with calls made in separate threads. The simple solution is to make sure that anything that interacts with the GUI uses Control.Invoke. On Oct 28, 2009, at 1:37 PM, Alex Shulgin wrote: > Hi all, > > Is System.Drawing by any means thread-safe? > > Can I create some threads, create a Graphics object in each of them > and then work with it from within that thread? Is this supposed to > work or am I doing something really stupid here? > > I ask because I've noticed random crashes in a WinForms app on OS X > (but it happens on Linux too). Most of the time it crashes in > System.Drawing.Graphics' DrawString or MeasureString methods and > seems to occur then a background worker thread is working in > parallel with UI thread. > > I've tried to do a stress-test of System.Drawing in a sample multi- > threaded program. See attached `test-multi-threaded-drawing.cs'. > > On my Linux box it crashes all the time. I get a wide variety of > errors from gdb stacktraces with SIGSEGV or SIGABRT in the end, to > SIGILL with .Net stack trace. > > Uncommenting these lock {} lines in the ThreadProc helps, but not an > option for my real app, as there's simply no single place a lock > could be added. > > I've also tried writing some code in plain C which links to > libgdiplus directly: see `threads-gdiplus.c'. It happily crashes > just like the C# version. > > My tests show that even using unsynchronized > GdipGetImageGraphicsContext / GdipDeleteGraphics (no fonts stuff > touched) can easily lead to crashes. > > From what I've seen, cairo seems to be thread-safe: see attached[1] > `cairo-multi-thread-text.c'. > > Also, there's a few locking used around thread-unsafe fontconfig > calls in libgdiplus itself. I didn't examined the whole code, so > there's possibly other places in it missing locking primitives. > > I would appreciate any help on this subject! > > -- > Regards, > Alex > [1] originally found in the cairo bugzilla for a few-years-old bug; > my sligthly enhanced version > /* gmcs test-multi-threaded-drawing.cs - > r:System.Drawing,System.Windows.Forms */ > using System; > using System.Text; > using System.Drawing; > using System.Windows.Forms; > using System.Threading; > > namespace test { > public class MainForm : Form { > private static int threadCount = 0; > > private object consoleLock = new Object(); > private object hwndLock = new Object(); > > public static void Main(string[] args) { > threadCount = args.Length == 0 ? 6 : int.Parse(args[0]); > > Application.Run(new MainForm()); > } > > protected override void OnLoad(EventArgs e) { > base.OnLoad(e); > > for (int i = 0; i < threadCount; ++i) { > Thread t = new Thread(ThreadProc); > t.Start(i); > } > } > > void ThreadProc(object data) { > int threadnum = (int) data; > > Random rnd = new Random((int) DateTime.Now.Ticks); > int count = rnd.Next(250, 1000); > lock (consoleLock) { > Console.WriteLine("thread{0} start: {1}", threadnum, count); > } > > for (int i = 0; i < count; ++i) { > string str = CreateRandomString(rnd); > > using (Graphics g = GetGraphicsForMeasurement()) { > using (Font font = CreateRandomFont(rnd)) { > //lock (hwndLock) { > SizeF sz = g.MeasureString(str, font); > > using (Bitmap bmp = new Bitmap((int) sz.Width, (int) > sz.Height)) { > using (Graphics gfx = Graphics.FromImage(bmp)) { > using (Brush b = Brushes.Red) { > gfx.DrawString(str, font, b, new PointF(0f, 0f)); > } > } > } > //} > } > } > } > > lock (consoleLock) { > Console.WriteLine("thread{0} done", threadnum); > } > } > > string CreateRandomString(Random rnd) { > int len = 1 + rnd.Next(60); > StringBuilder sb = new StringBuilder(len); > for (int j = 0; j < len; ++j) { > int ch = (rnd.Next() & 1) == 1 ? 0x41 : 0x61; // 'A' or 'a' > sb.Append(Char.ConvertFromUtf32(ch + rnd.Next(26))); > } > return sb.ToString(); > } > > Graphics GetGraphicsForMeasurement() { > #if NO_GRAPHICS_FROM_HWND > Bitmap tmp = new Bitmap(1, 1); > return Graphics.FromImage(tmp); > #else > Graphics g; > lock (hwndLock) { > g = Graphics.FromHwnd(this.Handle); > } > return g; > #endif > } > > Font CreateRandomFont(Random rnd) { > return new Font("Sans", (int) (8 + rnd.NextDouble()*10)); > } > } > } > /* gcc `pkg-config --cflags --libs cairo` -lpthread multi-thread- > text.c -o multi-thread-text */ > > /* > * Copyright © 2005 Red Hat, Inc. > * > * Permission to use, copy, modify, distribute, and sell this software > * and its documentation for any purpose is hereby granted without > * fee, provided that the above copyright notice appear in all copies > * and that both that copyright notice and this permission notice > * appear in supporting documentation, and that the name of > * Red Hat, Inc. not be used in advertising or publicity pertaining to > * distribution of the software without specific, written prior > * permission. Red Hat, Inc. makes no representations about the > * suitability of this software for any purpose. It is provided "as > * is" without express or implied warranty. > * > * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS > * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND > * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, > * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER > * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION > * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR > * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > * > * Author: Carl D. Worth <[hidden email]> > */ > > #include <assert.h> > #include <stdio.h> > #include <stdlib.h> > #include <cairo.h> > #include <pthread.h> > > static void * > start (void *closure) > { > int i; > > for (i = 0; i < 1000; ++i) { > cairo_surface_t *surface; > cairo_t *cr; > > surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 100, > 100); > cr = cairo_create (surface); > > cairo_move_to (cr, 10, 10); > > cairo_set_font_size (cr, 10); > cairo_show_text (cr, "Hello world.\n"); > > cairo_set_font_size (cr, 9); > cairo_show_text (cr, "Hello world.\n"); > > cairo_set_font_size (cr, 8); > cairo_show_text (cr, "Hello world.\n"); > > cairo_destroy (cr); > cairo_surface_destroy (surface); > } > > return NULL; > } > > int > main (int argc, char *argv[0]) > { > int i, num_threads; > pthread_t *pthread; > > if (argc > 1) { > num_threads = atoi (argv[1]); > } else { > num_threads = 6; > printf ("Running with default value of %d threads.\n" > "To change, call: %s <number_of_threads>\n", > num_threads, argv[0]); > } > > pthread = malloc (num_threads * sizeof (pthread_t)); > assert (pthread != NULL); > > for (i = 0; i < num_threads; i++) > pthread_create (&pthread[i], NULL, start, NULL); > > for (i = 0; i < num_threads; i++) { > pthread_join (pthread[i], NULL); > printf("joined thread%d\n", i); > } > > return 0; > } > /* gcc threads-gdiplus.c `pkg-config --cflags glib-2.0` -Wall - > lgdiplus -lpthread */ > > #include <assert.h> > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > #include <wchar.h> > > #include <gdiplus/GdiPlusFlat.h> > #include <pthread.h> > > pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; > > void > fatal(char const* func, GpStatus err) > { > fprintf(stderr, "%s: %d\n", func, err); > exit(1); > } > > static void* > start(void* closure) > { > GpStatus err; > > GpBitmap* img; > int width = 128; > int height = 32; > int stride = width*4; > > GpGraphics* gfx; > > GpFontFamily* family; > GpFont* font; > > WCHAR const str[] = {'H','e','l','l','o',',',' > ','W','o','r','l','d','!'}; > size_t len = sizeof(str)/sizeof(WCHAR); > RectF rect; > RectF bounds; > > int i; > > for (i = 0; i < 1000; ++i) { > > err = GdipCreateBitmapFromScan0(width, height, stride, > PixelFormat32bppARGB, /* scan0 = */ NULL, &img); > if (err != Ok) > fatal("GdipCreateBitmapFromScan0", err); > > //assert(pthread_mutex_lock(&mutex) == 0); > > err = GdipGetImageGraphicsContext(img, &gfx); > if (err != Ok) > fatal("GdipGetImageGraphicsContext", err); > > //assert(pthread_mutex_unlock(&mutex) == 0); > > > err = GdipGetGenericFontFamilySansSerif(&family); > if (err != Ok) > fatal("GdipGetGenericFontFamilySansSerif", err); > > err = GdipCreateFont(family, 12.0, FontStyleRegular, UnitPoint, > &font); > if (err != Ok) > fatal("GdipCreateFont", err); > > > > rect.X = 0; > rect.Y = 0; > rect.Width = width; > rect.Height = height; > > //assert(pthread_mutex_lock(&mutex) == 0); > > err = GdipMeasureString(gfx, str, len, font, &rect, /* format = > */ NULL, &bounds, > /* codepoints = */ NULL, /* lines = */ > NULL); > if (err != Ok) > fatal("GdipMeasureString", err); > > //assert(pthread_mutex_unlock(&mutex) == 0); > > > err = GdipDeleteFont(font); > if (err != Ok) > fatal("GdipDeleteFont", err); > > > > err = GdipDeleteGraphics(gfx); > if (err != Ok) > fatal("GdipDeleteGraphics", err); > > > err = GdipDisposeImage(img); > if (err != Ok) > fatal("GdipDisposeImage", err); > > } > > return NULL; > } > > int > main(int argc, char *argv[0]) > { > int i, num_threads; > pthread_t* pthread; > > GpStatus err; > ULONG_PTR gdiptok; > GdiplusStartupInput gdipinput; > GdiplusStartupOutput gdipoutput; > > err = GdiplusStartup(&gdiptok, &gdipinput, &gdipoutput); > if (err != Ok) > fatal("GdiplusStartup", err); > > > if (argc > 1) { > num_threads = atoi(argv[1]); > } else { > num_threads = 6; > printf("Running with default value of %d threads.\n" > "To change, call: %s <number_of_threads>\n", > num_threads, argv[0]); > } > > pthread = malloc(num_threads * sizeof(pthread_t)); > assert(pthread != NULL); > > for (i = 0; i < num_threads; i++) > pthread_create(&pthread[i], NULL, start, NULL); > > for (i = 0; i < num_threads; i++) { > pthread_join(pthread[i], NULL); > printf("joined thread%d\n", i); > } > > pthread_mutex_destroy(&mutex); > > return 0; > } > _______________________________________________ > Mono-devel-list mailing list > [hidden email] > http://lists.ximian.com/mailman/listinfo/mono-devel-list _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
|
Alex Shulgin
|
In reply to this post
by Sebastien Pouliot
Sebastien Pouliot wrote:
> > Please open a bug report on bugzilla.novell.com and attach your test > case. https://bugzilla.novell.com/show_bug.cgi?id=551009 >> On my Linux box it crashes all the time. I get a wide variety of errors >> from gdb stacktraces with SIGSEGV or SIGABRT in the end, to SIGILL with >> .Net stack trace. >> >> Uncommenting these lock {} lines in the ThreadProc helps, but not an >> option for my real app, as there's simply no single place a lock could >> be added. >> >> I've also tried writing some code in plain C which links to libgdiplus >> directly: see `threads-gdiplus.c'. It happily crashes just like the C# >> version. > > Most of System.Drawing is only a small wrapper abound libgdiplus (or GDI > + under Windows). Any bug will likely be inside libgdiplus so it's > "normal" (i.e. expected) that you get the same behavior. Yes, it's what I expected. Just thought it might be helpful in debugging the problem. Attached this to the bug too. >> My tests show that even using unsynchronized GdipGetImageGraphicsContext >> / GdipDeleteGraphics (no fonts stuff touched) can easily lead to crashes. >> >> From what I've seen, cairo seems to be thread-safe: see attached[1] >> `cairo-multi-thread-text.c'. > > It's thread-safe as long as everything is created and executed in the > same thread. Otherwise it would not be safe-thread (just like .net and > most libraries are). This is actually what I meant by thread-safe here. :) I'd expect any library not to crash under similar circumstances as long as it doesn't use global variables or, if so, does ensure proper locking around them. -- Regards, Alex _______________________________________________ Mono-devel-list mailing list [hidden email] http://lists.ximian.com/mailman/listinfo/mono-devel-list |
||||||||||||||||
| Free Embeddable Forum Powered by Nabble | Help |