Bug 6029 - Stream position in callback method called from unmanaged code
Summary: Stream position in callback method called from unmanaged code
Alias: None
Product: Runtime
Classification: Mono
Component: Interop ()
Version: unspecified
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Bugzilla
Depends on:
Reported: 2012-07-07 07:34 UTC by pinggi
Modified: 2017-07-07 18:58 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

Code example (3.09 KB, text/plain)
2012-07-07 07:37 UTC, pinggi

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description pinggi 2012-07-07 07:34:37 UTC
I encountered following behavior and it might be wrong:

There is a Stream object. A managed callback function which reads data from the Stream object is passed to unmanaged function. Unmanaged function then calls the managed callback several times for getting data from the stream.

The data from the stream are read and stream position is changed correctly.
But when the unmanaged code calls the callback again, the position of the stream is lost. Position is zero and should be greater than zero because it was already read from the stream. So the reading itself starts from the beginning again.

It seems that stream is somehow reset or created again. I don't understand. It might be a bug in Mono runtime or Stream or marshaling.

Stream is hold in a static field of the class where the callback function is defined. It cannot be passed directly to the callback function because there is IntPtr parametr and Stream is object. Mono runtime can't marshall an object to an IntPtr and back. Also pinned gc handle is not possible to used because Stream is complex type and cannot be serialized. I don't know about other ways of passing object to IntPtr argument so I pass the Stream object in static field (I know that it isn't good for multithreaded apps).

The callback delegate:

private delegate Cairo.Status cairo_read_func_t(
IntPtr closure, IntPtr data, int length);

The unmanaged function declaration:

private static extern IntPtr cairo_image_surface_create_from_png_stream(
cairo_read_func_t read_func, IntPtr closure);

The static field:

private static Stream stream_hack;

Method that calls unmanaged cairo_image_surface_create_from_png_stream:

public static Cairo.ImageSurface CreateImageSurfaceFromPngStream(Stream stream)
	Cairo.ImageSurface newSurf;
	// HACK: stream is passed to callback through static field
	// it can't be passed as closure parameter in cairo_image_surface_create_from_png_stream
	// because 1. object/IntPtr marshaling doesn't work, 2. gc handle pinned object is too complex and I don't know how to do it in other way currently
	stream_hack = stream;

	IntPtr handle = cairo_image_surface_create_from_png_stream(StreamFeederFce, IntPtr.Zero); // here, Cairo will call StreamFeederFce several times

The managed callback method:
public static Cairo.Status StreamFeederFce(IntPtr closure, IntPtr data, int length)
	byte[] mdata = new byte[length];
	// stream_hack.Position == 0 - every time when StreamFeederFce is called (why the position is reset? it reads the beginning of the stream again and again)
	int read = stream_hack.Read(mdata, 0, length);
	// stream_hack.Position == length - after Read call position is 8 which is correct, but it is not remembered for next call of StreamFeederFce, why?

	if (read != length) return Cairo.Status.ReadError;

	Marshal.Copy(mdata, 0, data, length);

	return Cairo.Status.Success;
Comment 1 pinggi 2012-07-07 07:37:39 UTC
Created attachment 2171 [details]
Code example
Comment 2 pinggi 2012-07-07 08:03:17 UTC
Mmm, I've found out that it is not the problem in Mono. 

I thought that the callback is called more times, but it isn't. I loaded more images and the callback is called only once for every image.

Cairo calls the callback only once, read 8 bytes and returns empty ImageSurface (width 0, height 0, etc.).

So the problem migth be in Cairo or in the data passing. The same data are loaded correctly from the file by ImageSurface constructor, but only 8 bytes are read from stream by calling cairo_image_surface_create_from_png_stream.
Comment 3 Ludovic Henry 2017-07-07 18:58:53 UTC
If you can still reproduce with latest mono version, please feel free to reopen the bug. Thank you.