How to stop a background thread when an image window is closed in dm-script - dm-script

I'm trying to write a dm-script containing a background thread as following example code. In this code, I want to stop the background thread when the image window is closed. I think that some event-listener is probably required in this case. Could you advise me about how to control (i.e., stop) a background thread by closing event of an image window? If you could suggest some revision of my code or show your example code, I would be grateful for your cooperation.
// $BACKGROUND$
//
Class CBackground : Thread
{
Number isRunning
Number imgID
Image tmpIMG
//
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackground)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()

And here is an example of a script which uses a window-closed listener to abort the task.
Class CBackgroundWithListeners
{
Number isRunning
Number imgID
Image tmpIMG
Number winListenID
// Constructor and Destructor method for debugging reason
// Always automatically called when object gets created or removed from memory
CBackgroundWithListeners(object self) {
Result( "\n Creating object " + self.ScriptObjectGetClassName() )
Result( " with ID: " + self.ScriptObjectGetID() )
}
~CBackgroundWithListeners(object self) {
Result( "\n Destroying object " + self.ScriptObjectGetClassName() )
Result( " with ID: " + self.ScriptObjectGetID() )
}
// Init Method
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
If ( !tmpIMG.ImageIsValid() )
Throw( "Image of ID " + imgID + " not found." )
if ( 0 == tmpIMG.ImageCountImageDisplays() )
Throw( "Image of ID " + imgID + " has no display." )
DocumentWindow win = tmpIMG.ImageGetOrCreateImageDocument().ImageDocumentGetWindow()
if ( !win.WindowIsValid() )
Throw( "Image of ID " + imgID + " has no window." )
winListenID = win.WindowAddWindowListener( self, "window_closed:HandleClosedAction;" )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
//
Void HandleClosedAction(object self, number e_fl, DocumentWindow Win)
{
self.StopRunning()
win.WindowRemoveWindowListener( winListenID )
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackgroundWithListeners)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()

You can not stop a background thread from outside that thread, i.e. you can not interrupt a thread. In order to stop a background thread, the according code needs to have a stop-condition and exit itself.
In order to steer this stop-condition from another thread, you need to "communicate" somehow between two threads. This can be done in many different ways. The easiest is to use a simple number variable which is checked by the background running code but can be set via any other "outside" code. An example for this can be found in this answer here.
Instead of a simple variable, one could also use some commonly accessible place like f.e. the global tags. Alternatively, some more sophistacted threading synchronisation objects like signals, mutexes and semphores are defined for the scripting language and describe in the help documentation here:
How an external thread inserts the 'break' into the background running thread can also be done in many different ways. One is - as in the exampe above - have a user interaction via some open dialog. Another, as mentioned by the author, is to have some event-listener code to trigger this.
The following example attaches a key-listener to the image, so that (with this image frontmost and selected) a user can press the ESC button to stop the thread.
I am using the provided script with minimum modifications for showing this:
// $BACKGROUND$
//
Class CBackground : Thread
{
Number isRunning
Number imgID
Number keyListenID
Image tmpIMG
//
Void Init( Object self, Number iID ){
imgID = iID
tmpIMG := GetImageFromID( imgID )
ImageDisplay disp = tmpIMG.ImageGetImageDisplay(0)
keyListenID = disp.ImageDisplayAddKeyHandler( self, "KeyListenAction" )
}
//
Void StopRunning( Object self ){
isRunning = 0
}
//
Number GetIsRunning( Object self ){
return isRunning
}
/////////////////////////////////////////////////////////////////////////////
Number KeyListenAction(Object self, ImageDisplay disp, Object keydesc )
{
number b_keyhandled = 0
If ( keydesc.MatchesKeyDescriptor("esc") )
{
disp.ImageDisplayRemoveKeyHandler( keyListenID )
self.StopRunning()
Result( "\nSend stopping flag, unregister Key-Listeners" )
b_keyhandled = 1
}
return b_keyhandled;
}
//
Void RunThread( Object self ){
Result("Background thread is starting ......")
isRunning = 1
while (isRunning)
{
tmpIMG = random()
sleep(0.5)
}
Result(" finished !!" + "\n")
}
}
//
Void Main(){
Object cbkg = alloc(CBackground)
Image IMG := RealImage("test",4,64,64)
IMG = random()
IMG.ShowImage()
IMG.SetWindowSize(512,512)
cbkg.Init(IMG.GetImageID())
cbkg.StartThread()
}
//
Main()
However, there are a few things I would do differently:
Start the background thread with the proper command rather then the old '$$BACKGROUND$$' method.
Encapsulate as much as possible into the class
Add a few security checkes
Also add a windows closed event listener, so that closing the image window also stops the thread
Add some debug-code to show when an object is created and when it is removed from memory
Have the keylistener to pause/unpause the action.

Related

Error "Out of segment space" in VMEmulator cause by a getter mwthod in Jack

I am doing a project for nand2tetris. We write a program in Jack and test it on VMEmulator. The class looks like this:
class List {
field int data;
field List next;
/* Creates a new List object. */
constructor List new(int car, List cdr) {
let data = car;
let next = cdr;
return this;
}
/* Disposes this List by recursively disposing its tail. */
method void dispose() {
if (~(next = null)) {
do next.dispose();
}
// Use an OS routine to recycle the memory held by this object.
do Memory.deAlloc(this);
return;
}
/* Prints the list*/
method void print() {
do Output.printString(" -> ");
do Output.printInt(data);
if (~(next = null)) {
do next.print();
}
return;
}
/* Inserts the argument in the right position of the list (ascending order)*/
method void insertInOrder(int ins){
var List prev, curr, insert;
let prev = this;
let curr = prev.getnext();
while (ins > prev.getdata()){
if (ins < curr.getdata()){
let insert = List.new(ins, curr);
do prev.setnext(insert);
}
else{
let prev = prev.getnext();
let curr = prev.getnext();
}
}
return;
}
/* Searches the argument in the list, if found, it returns the corresponding List object*/
method List find(int toFind){
var List temp;
var List equal;
var boolean found;
let temp = this;
let found = false;
while (~(next = null)){
if(toFind = temp.getdata()){
let equal = temp;
let found = true;
}
let temp = temp.getnext();
}
if (found){
return equal;
}
else{
return null;
}
}
method List getnext(){
return next;
}
method void setnext(List object){
let next = object;
return;
}
method int getdata(){
return data;
}
}
It has one private variable data and a pointer next. So I wrote getter and setter method to return those values. Other methods are fine only the getdata()method is incorrect. When it runs through the VMEmulator, it shows the error Out of segment space in List.getdata.3. This shows in the VMEmulator.
0function List.getdata0
1push argument0
2pop pointer0
3push this 0
4return
the error is at the 4th line return. When I change the Jack code, the same error is still at the 4th line.
What exactly is the problem in my getter method?
When you run a VM program on the VMEmulator you must first manually set the pointers to the various segments, otherwise you may get an "Out of segment space" error.
To understand the necessary settings, look at what the corresponding .tst file does. An alternative method is to insert the proposed code inside a function, since the function call automatically makes this type of setting.
You can get this error when you try to access member data of an object which is not constructed. Could it be that the List cdr in the constructor was not properly constructed?

add a object to a array in my class

I want to add an object (from a different class) to an array in my class.
When I try to do that I get this
error: 0xC0000005: Access violation writing location
0x0000000000000000
I create the object (to be added) in the main function and I use the push method in the main function to add this object to my Parking_Lot class.
My code:
void Parking_Lot::push(Cars const &car)
{
time_t t = time(NULL);
struct tm Today = *localtime(&t);
if (is_full())
{
printf("Parking lot is full!!\n");
return;
}
if (Today.tm_hour < OpeningT.tm_hour && Today.tm_hour > ClosingT.tm_hour)
{
printf("Parking lot is now closed!!\n");
printf("Opening time: from %02d:%02d to %02d:%02d\n", OpeningT.tm_hour, OpeningT.tm_min, ClosingT.tm_hour, ClosingT.tm_min);
}
else if (Today.tm_hour == OpeningT.tm_hour || Today.tm_hour == ClosingT.tm_hour)
{
if(Today.tm_min > OpeningT.tm_min || Today.tm_min < ClosingT.tm_min) Lot[front++] = car;
else
{
printf("Parking lot is now closed!!\n");
printf("Opening time: from %02d:%02d to %02d:%02d\n", OpeningT.tm_hour, OpeningT.tm_min, ClosingT.tm_hour, ClosingT.tm_min);
}
}
else if(Today.tm_hour > OpeningT.tm_hour && Today.tm_hour < ClosingT.tm_hour) Lot[front++] = car;
}
where the car is the object I want to add and the Lot is the array in my class that I want to add the object to.
The constructor of my class:
Parking_Lot::Parking_Lot(int s)
{
Cars* Lot = new Cars[s+1];
size = s;
front = 0;
}
What am i doing wrong here and how can I fix it?
The problem is in your constructor:
Parking_Lot::Parking_Lot(int s)
{
Cars* Lot = new Cars[s+1];
size = s;
front = 0;
}
You define a new and separate variable Lot inside the constructor. It will not be related to any possible member variable you might have with the same name.
You need to initialize the member variable instead:
Parking_Lot::Parking_Lot(int s)
{
Lot = new Cars[s+1];
size = s;
front = 0;
}

Dynamic Bytecode Instrumentation fails without any error

Objective
I'm doing dynamic bytecode instrumentation using a JVMTI agent. I have to instrument those methods which are "hot", that is, the methods which invoke JIT compiler. To do so I listen to a CompiledLoadEvent and inside its call back function, call RetransformClasses. This in turn invokes ClassFileLoadHook on the class containing "hot" function and actual instrumentation begins.
Problem Premises
Currently I'm instrumenting my class to spawn some threads. I also listen to thread starts and print them within my agent. With simple ClassFileLoadHook at class load time (without RetransformClasses), my instrumentation works perfectly and spawns new threads. I get following output when ClassFileLoadHook instruments at class load time:
Running Thread: Signal Dispatcher, Priority: 9, context class loader:Not Null
Running Thread: main, Priority: 5, context class loader:Not Null
Running Thread: Thread-0, Priority: 5, context class loader:Not Null
Running Thread: Thread-1, Priority: 5, context class loader:Not Null
Running Thread: Thread-2, Priority: 5, context class loader:Not Null
Running Thread: Thread-3, Priority: 5, context class loader:Not Null
Running Thread: Thread-4, Priority: 5, context class loader:Not Null
Running Thread: Thread-6, Priority: 5, context class loader:Not Null
Running Thread: Thread-5, Priority: 5, context class loader:Not Null
Running Thread: Thread-7, Priority: 5, context class loader:Not Null
Running Thread: DestroyJavaVM, Priority: 5, context class loader:: NULL
When I instrument the class file by invoking RetransformClasses and then ClassFileLoadHook, everything works fine but no threads are spawned and hence no effective instrumentation takes place. VM takes a long time even to execute the original code.
I double checked both instrumentations using -XX:+TraceClassLoading. All the retransformed classes are loaded in both cases. Even the class I'm generating during runtime also gets loaded but no instrumentation happens. Below is the output of class loading trace:
[Loaded Test from __VM_RedefineClasses__]
[Loaded Test_Worker_main_0 from file:/home/saqib/workspace/test/bin]
I'm generating second class during runtime and it loads into VM but I don't get any thread spawning.
Questions
Given my understanding of the problem (There is a high probability
that I'd be wrong), why ClassFileLoadHook retransforms the class
successfully during load time, but somehow doesn't behave correctly
when JIT is invoked?
Just writing the RetransformClasses function, with empty
ClassFileLoadHook call back, also takes a lot of time without
incurring any sort of error. What could be taking time?
Agent Code
Compiled Load Event Call Back
static int x = 1;
void JNICALL
compiled_method_load(jvmtiEnv *jvmti, jmethodID method, jint code_size,
const void* code_addr, jint map_length, const jvmtiAddrLocationMap* map,
const void* compile_info) {
jvmtiError err;
jclass klass;
char* name = NULL;
char* signature = NULL;
char* generic_ptr = NULL;
err = (*jvmti)->RawMonitorEnter(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor enter");
err = (*jvmti)->GetMethodName(jvmti, method, &name, &signature,
&generic_ptr);
check_jvmti_error(jvmti, err, "Get Method Name");
printf("\nCompiled method load event\n");
printf("Method name %s %s %s\n\n", name, signature,
generic_ptr == NULL ? "" : generic_ptr);
if (strstr(name, "main") != NULL && x == 1) {
x++;
err = (*jvmti)->GetMethodDeclaringClass(jvmti, method, &klass);
check_jvmti_error(jvmti, err, "Get Declaring Class");
err = (*jvmti)->RetransformClasses(jvmti, 1, &klass);
check_jvmti_error(jvmti, err, "Retransform class");
}
if (name != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) name);
check_jvmti_error(jvmti, err, "deallocate name");
}
if (signature != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) signature);
check_jvmti_error(jvmti, err, "deallocate signature");
}
if (generic_ptr != NULL) {
err = (*jvmti)->Deallocate(jvmti, (unsigned char*) generic_ptr);
check_jvmti_error(jvmti, err, "deallocate generic_ptr");
}
err = (*jvmti)->RawMonitorExit(jvmti, lock);
check_jvmti_error(jvmti, err, "raw monitor exit");
}
Class File Load Hook
void JNICALL
Class_File_Load_Hook(jvmtiEnv *jvmti_env, JNIEnv* jni_env,
jclass class_being_redefined, jobject loader, const char* name,
jobject protection_domain, jint class_data_len,
const unsigned char* class_data, jint* new_class_data_len,
unsigned char** new_class_data) {
jvmtiError err;
unsigned char* jvmti_space = NULL;
if (strstr(name, "Test") != NULL && x == 2) {
char* args = "op";
javab_main(2, args, class_data, class_data_len);
err = (*jvmti_env)->Allocate(jvmti_env, (jlong)global_pos, &jvmti_space);
check_jvmti_error(jvmti_env, err, "Allocate new class Buffer.");
(void)memcpy((void*)jvmti_space, (void*)new_class_ptr, (int)global_pos);
*new_class_data_len = (jint)global_pos;
*new_class_data = jvmti_space;
if ( new_class_ptr != NULL ) {
(void)free((void*)new_class_ptr);
}
#if DEBUG
printf("Size of the class is: %d\n", class_data_len);
for (int i = 0; i < class_data_len; i += 4) {
if (i % 16 == 0)
printf("\n");
printf("%02x%02x %02x%02x ", new_class_data[i],
new_class_data[i + 1], new_class_data[i + 2],
new_class_data[i + 3]);
}
printf("\n");
system("javap -c -v Test_debug");
#endif
x++;
}
}
Here javab_main returns the instrumented char * array which is correct. The instrumented array is stored in a global variable new_class_ptr which is copied into new_class_data. To debug the output of the instrumentation, I also printed the instrumented class in a file called Test_debug and invoking javap on it produces desired result.
The complete agent file is given here:
Agent.c
Original Code:
for (int i = 0; i < s; i++)
for (int j = 0; j < s; j++) {
c2[i][j] = 0;
for (int k = 0; k < s; k++)
c2[i][j] += a[i][k] * b[k][j];
}
Instrumented Code: (Equivalent)
Thread[] threads = new Thread[NTHREADS];
for (int i = 0; i < NTHREADS ; i++) {
final int lb = i * SIZE/NTHREADS;
final int ub = (i+1) * SIZE/NTHREADS;
threads[i] = new Thread(new Runnable() {
public void run() {
for (int i = lb; i < ub; i++)
for (int j = 0; j < SIZE; j++) {
c2[i][j] = 0;
for (int k = 0; k < SIZE; k++)
c2[i][j] += a[i][k] * b[k][j];
}
}
});
threads[i].start();
}
// wait for completion
for (int i = 0; i < NTHREADS; i++) {
try {
threads[i].join();
} catch (InterruptedException ignore) {
}
}
Java Version
openjdk version "1.8.0-internal-debug"
OpenJDK Runtime Environment (build 1.8.0-internal-debug-saqib_2016_12_26_10_52-b00)
OpenJDK 64-Bit Server VM (build 25.71-b00-debug, mixed mode)
I'm constructing this answer mainly from the comments. There are still some riddles unsolved but the main question has been resolved. Bytecode instrumentation does not fail in my case. It actually never happens. According to the theory,
Dynamic bytecode instrumentation of an executing function takes place
at subsequent call of the function. If a function has only one invocation, it cannot be instrumented while execution using current hotswap techniques in JVM.
I was trying to instrument a class which had only one function, i.e. main. I was trying to instrument that during runtime. This was the main problem. To check the validity of this argument, I tried to put my code in another function and called it from main in a loop. It got instrumented and everything worked. Nothing has to be changed in the agent.

Defining a series of functions in DigitalMicrograph scripting

I have a set of functions inside a class that I need to define. Each passes a different value into another function:
void function00(object self, taggroup tg) self.otherfunction(tg,0,0)
void function01(object self, taggroup tg) self.otherfunction(tg,0,1)
void function02(object self, taggroup tg) self.otherfunction(tg,0,2)
void function03(object self, taggroup tg) self.otherfunction(tg,0,3)
void function04(object self, taggroup tg) self.otherfunction(tg,0,4)
I have 100 of these functions and I'd prefer not to define each one separately. Considering the above example I'd like to do something like:
for(number i=0; i<5; i++){
void function0+i(object self, taggroup tg) self.otherfunction(tg,0,i)
}
which doesn't work on it's own. Any suggestions?
For some more context I create a series of check boxes inside 2 for loops with the following:
BOXinsides.DLGAddElement(DLGCreateCheckBox(label,0,"function"+i+j).DLGIdentifier("#function"+i+j))
and I need to define all the functions in some sensible way.
DigitalMicrograph scripting does not allow this type of template code. However, you can solve your problem by linking all checkbox items to the same action-method. The signature of the action method passed in the TagGroup which is the checkbox item itself. You can use this to derive information from it, for example by looking at a checkbox property such as its title:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number i = val( right( label, len( label ) - 1 ) )
Result( "\n running index:" + i )
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0 ; I < 5 ; i++ )
{
checkboxGroup.DLGAddElement( DLGCreateCheckBox( "C" + I , 0 , "generalFunction" ) )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg = Alloc(myUI).Init()
dlg.pose()
}
You can also 'attach' information directly to the checkbox. Checkboxes are - as all dialog items - really just specific TagGroup objects to which you can add whatever you like. In the example below, I'm adding an additional tag with a random number:
class myUI : UIframe
{
void generalFunction( object self , tagGroup checkTg )
{
// checkTg is the taggroup of the checkbox which fired the method.
// Use its Title to get back the running value!
string label = checkTg.DLGGetTitle()
Result( "\n label of checkbox:" + label )
number rnd
if ( checkTG.TagGroupGetTagAsNumber( "Random NR", rnd ) )
{
Result( "\n Random number:" + rnd )
}
}
TagGroup CreateCheckboxes( object self )
{
TagGroup checkboxGroup = DLGCreateGroup()
for ( number i = 0; I < 5 ; i++ )
{
TagGroup checkbox = DLGCreateCheckBox( "C" + I , 0 , "generalFunction" )
checkbox.TagGroupSetTagAsNumber( "Random NR", Random() )
checkboxGroup.DLGAddElement( checkbox )
}
return checkboxGroup
}
TagGroup CreateDLGTags( object self )
{
TagGroup dlg, dlgitems
dlg = DLGCreateDialog( "Test" , dlgitems )
dlgitems.DLGAddElement( self.CreateCheckboxes() )
return dlg
}
object Init( object self )
{
return self.super.init( self.CreateDLGTags() )
}
}
// MAIN SCRIPT calling the dialog
{
Object dlg=Alloc(myUI).Init()
dlg.pose()
}

Memory issue using CGImage.ScreenImage and UIImagePickerController Mono Touch

I'm trying to create an IP Camera application for iPhone using mono touch. To get the camera feed, I call the CGImage.ScreenImage method continuously and convert the resulting UIImage into a byte array which is then transmitted across to the browser. The camera feed is displayed using a UIImagePickerController class by setting the
SourceType = UIImagePickerControllerSourceType.Camera;
Here are the complete code for the ImageController class:
public class ImageController : UIImagePickerController
{
private HttpListener listener;
public ImageController ()
{
SourceType = UIImagePickerControllerSourceType.Camera;
start();
}
void start()
{
listener = new HttpListener();
listener.Prefixes.Add( "http://+:8001/" );
listener.Prefixes.Add( "http://+:8001/current.jpg/" );
listener.Start();
listener.BeginGetContext( HandleRequest, listener );
}
public override void DidReceiveMemoryWarning ()
{
GC.Collect();
}
private void HandleRequest(IAsyncResult result)
{
HttpListenerContext context = listener.EndGetContext( result );
listener.BeginGetContext( HandleRequest, listener );
if ( context.Request.RawUrl.Contains( "current.jpg" ) )
{
context.Response.StatusCode = (int) HttpStatusCode.OK;
context.Response.ContentType = #"multipart/x-mixed-replace;boundary=--myboundary";
context.Response.KeepAlive = true;
byte [] imageData;
byte [] boundary;
while ( true )
{
imageData = worker();
boundary =
Encoding.UTF8.GetBytes(
"\r\n--myboundary\r\nContent-Type: image/jpeg\r\nContent-Length: " +
imageData.Length + "\r\n\r\n" );
context.Response.OutputStream.Write( boundary, 0, boundary.Length );
context.Response.OutputStream.Write( imageData, 0, imageData.Length );
imageData = null;
boundary = null;
}
}
else
{
string responseString = #"<html xmlns=""http://www.w3.org/1999/xhtml""> <body bgcolor=""#fffffff"">"+
#"<img name=""current"" src=""current.jpg"" border=""0"" id=""current"" /></body></html>";
byte [] responseByte = Encoding.UTF8.GetBytes( responseString );
context.Response.ContentType = "text/html";
context.Response.KeepAlive = true;
context.Response.StatusCode = (int) HttpStatusCode.OK;
context.Response.ContentLength64 = responseByte.Length;
context.Response.OutputStream.Write( responseByte, 0, responseByte.Length );
context.Response.OutputStream.Close();
}
}
private byte [] worker()
{
byte [] imageArray;
using ( var screenImage = CGImage.ScreenImage )
{
using ( var image = UIImage.FromImage(screenImage) )
{
using ( NSData imageData = image.AsJPEG())
{
imageArray = new byte[imageData.Length];
Marshal.Copy(imageData.Bytes, imageArray, 0, Convert.ToInt32(imageData.Length));
}
}
}
return imageArray;
}
}
}
The problem I'm having is that I receive a memory warning after about 20 seconds of streaming and a second one after 5 seconds, then the application crashes.
2010-11-08 13:12:56.457 MonoTouchCGImageScreenImageTest[2251:307] Received memory warning. Level=1
2010-11-08 13:13:11.059 MonoTouchCGImageScreenImageTest[2251:307] Received memory warning. Level=2
All of the images that were displayed have been disposed of within the using statement and the imageData variable is also been set to null once it is transmitted.
Is there some method that needs to be called or is there a better solution to my problem? Any help would be appreciated.
Thanks
You are calling worker() in a never ending loop:
while ( true )
{
imageData = worker();
boundary =
Encoding.UTF8.GetBytes(
"\r\n--myboundary\r\nContent-Type: image/jpeg\r\nContent-Length: " +
imageData.Length + "\r\n\r\n" );
context.Response.OutputStream.Write( boundary, 0, boundary.Length );
context.Response.OutputStream.Write( imageData, 0, imageData.Length );
imageData = null;
boundary = null;
}
Which means that even tho you are disposing, you're in a tight allocation loop which will put a lot of pressure on the GC.
Additionally, you are using UIImage.FromImage() which returns an autoreleased object from the obj-c runtime, but since you never yield to the main loop, the NSAutoreleasePool cannot release this object.