Diff of /trunk/src/net_io.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 4 by harbaum, Wed Dec 10 19:50:17 2008 UTC revision 7 by harbaum, Thu Dec 11 20:34:41 2008 UTC
# Line 47  typedef struct { Line 47  typedef struct {
47    int len;    int len;
48  } curl_mem_t;  } curl_mem_t;
49    
50  typedef enum { NET_IO_FILE, NET_IO_MEM } net_io_type_t;  typedef enum { NET_IO_DL_FILE, NET_IO_DL_MEM, NET_IO_DELETE } net_io_type_t;
51    
52    /* structure shared between worker and master thread */
53  typedef struct {  typedef struct {
54    net_io_type_t type;    net_io_type_t type;
55    gint refcount;       /* reference counter for master and worker thread */    gint refcount;       /* reference counter for master and worker thread */
56    
57    char *url;    char *url, *user;
58    gboolean cancel;    gboolean cancel;
59    float progress;    float progress;
60    
# Line 62  typedef struct { Line 63  typedef struct {
63    long response;    long response;
64    char buffer[CURL_ERROR_SIZE];    char buffer[CURL_ERROR_SIZE];
65    
66      /* request specific fields */
67    union {    union {
68      char *filename;      char *filename;   /* used for NET_IO_DL_FILE */
69      curl_mem_t mem;      curl_mem_t mem;   /* used for NET_IO_DL_MEM */
70    };    };
71    
72  } net_io_request_t;  } net_io_request_t;
# Line 123  static GtkWidget *busy_dialog(GtkWidget Line 125  static GtkWidget *busy_dialog(GtkWidget
125  }  }
126    
127  static void request_free(net_io_request_t *request) {  static void request_free(net_io_request_t *request) {
128    g_free(request->url);    /* decrease refcount and only free structure if no references are left */
129    if(request->type == NET_IO_FILE)    request->refcount--;
130      if(request->refcount) {
131        printf("still %d references, keeping request\n", request->refcount);
132        return;
133      }
134    
135      printf("no references left, freeing request\n");
136      if(request->url)  g_free(request->url);
137      if(request->user) g_free(request->user);
138    
139      /* filename is only a valid filename in NET_IO_DL_FILE mode */
140      if(request->type == NET_IO_DL_FILE && request->filename)
141      g_free(request->filename);      g_free(request->filename);
142    
143    g_free(request);    g_free(request);
# Line 160  static void *worker_thread(void *ptr) { Line 173  static void *worker_thread(void *ptr) {
173      FILE *outfile = NULL;      FILE *outfile = NULL;
174      gboolean ok = FALSE;      gboolean ok = FALSE;
175    
176      if(request->type == NET_IO_FILE) {      /* prepare target (file, memory, ...) */
177        switch(request->type) {
178        case NET_IO_DL_FILE:
179        outfile = fopen(request->filename, "w");        outfile = fopen(request->filename, "w");
180        ok = (outfile != NULL);        ok = (outfile != NULL);
181      } else {        break;
182    
183        case NET_IO_DL_MEM:
184        request->mem.ptr = NULL;        request->mem.ptr = NULL;
185        request->mem.len = 0;        request->mem.len = 0;
186        ok = TRUE;        ok = TRUE;
187          break;
188    
189        default:
190          ok = TRUE;
191          break;
192      }      }
193    
194      if(ok) {      if(ok) {
195        curl_easy_setopt(curl, CURLOPT_URL, request->url);        curl_easy_setopt(curl, CURLOPT_URL, request->url);
196        if(request->type == NET_IO_FILE) {  
197          switch(request->type) {
198          case NET_IO_DL_FILE:
199          curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);          curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
200          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
201        } else {          break;
202    
203          case NET_IO_DL_MEM:
204          curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);          curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);
205          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);          curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);
206            break;
207    
208          case NET_IO_DELETE:
209            curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
210            break;
211        }        }
212    
213          /* set user name and password for the authentication */
214          if(request->user)
215            curl_easy_setopt(curl, CURLOPT_USERPWD, request->user);
216    
217          /* setup progress notification */
218        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
219        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_progress_func);        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_progress_func);
220        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, request);        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, request);
221    
222        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, request->buffer);        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, request->buffer);
223    
224          /* play nice and report some user agent */
225          curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
226    
227        request->res = curl_easy_perform(curl);        request->res = curl_easy_perform(curl);
228        printf("thread: curl perform returned with %d\n", request->res);        printf("thread: curl perform returned with %d\n", request->res);
229    
230        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response);        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response);
231    
232        if(request->type == NET_IO_FILE)        if(request->type == NET_IO_DL_FILE)
233          fclose(outfile);          fclose(outfile);
234      }      }
235    
# Line 197  static void *worker_thread(void *ptr) { Line 238  static void *worker_thread(void *ptr) {
238    }    }
239    
240    printf("thread: io done\n");    printf("thread: io done\n");
241      request_free(request);
   if(request->refcount < 2) {  
     printf("thread: master has prematurely released request\n");  
   
     request_free(request);  
   } else {  
     printf("thread: informing master about result\n");  
   
     request->refcount--;  
   }  
242    
243    printf("thread: terminating\n");    printf("thread: terminating\n");
244    return NULL;    return NULL;
245  }  }
246    
247  static gboolean net_io_do(GtkWidget *parent, net_io_request_t *request) {  static gboolean net_io_do(GtkWidget *parent, net_io_request_t *request) {
248      /* the request structure is shared between master and worker thread. */
249      /* typically the master thread will do some waiting until the worker */
250      /* thread returns. But the master may very well stop waiting since e.g. */
251      /* the user activated some cancel button. The client will learn this */
252      /* from the fact that it's holding the only reference to the request */
253    
254    GtkWidget *pbar = NULL;    GtkWidget *pbar = NULL;
255    GtkWidget *dialog = busy_dialog(parent, &pbar, &request->cancel);    GtkWidget *dialog = busy_dialog(parent, &pbar, &request->cancel);
256    
# Line 222  static gboolean net_io_do(GtkWidget *par Line 260  static gboolean net_io_do(GtkWidget *par
260      g_warning("failed to create the worker thread");      g_warning("failed to create the worker thread");
261    
262      /* free request and return error */      /* free request and return error */
263      request_free(request);      request->refcount--;    /* decrease by one for dead worker thread */
264      gtk_widget_destroy(dialog);      gtk_widget_destroy(dialog);
265      return FALSE;      return FALSE;
266    }    }
# Line 241  static gboolean net_io_do(GtkWidget *par Line 279  static gboolean net_io_do(GtkWidget *par
279        if(!progress)        if(!progress)
280          gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pbar));          gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pbar));
281    
282      usleep(10000);      usleep(100000);
283    }    }
284    
285    gtk_widget_destroy(dialog);    gtk_widget_destroy(dialog);
# Line 249  static gboolean net_io_do(GtkWidget *par Line 287  static gboolean net_io_do(GtkWidget *par
287    /* user pressed cancel */    /* user pressed cancel */
288    if(request->refcount > 1) {    if(request->refcount > 1) {
289      printf("operation cancelled, leave worker alone\n");      printf("operation cancelled, leave worker alone\n");
   
     /* remove the file that may have been written by now. the kernel */  
     /* should cope with the fact that the worker thread may still have */  
     /* an open reference to this and might thus still write to this file. */  
     /* letting the worker delete the file is worse since it may take the */  
     /* worker some time to come to the point to delete this file. If the */  
     /* user has restarted the download by then, the worker will erase that */  
     /* newly written file */  
     g_remove(request->filename);  
   
     request->refcount--;  
290      return FALSE;      return FALSE;
291    }    }
292    
# Line 267  static gboolean net_io_do(GtkWidget *par Line 294  static gboolean net_io_do(GtkWidget *par
294    
295    /* --------- evaluate result --------- */    /* --------- evaluate result --------- */
296    
297      /* the http connection itself may have failed */
298    if(request->res != 0) {    if(request->res != 0) {
299      errorf(parent, _("Download failed with message:\n\n%s"), request->buffer);      errorf(parent, _("Download failed with message:\n\n%s"), request->buffer);
     g_remove(request->filename);  
     request_free(request);  
300      return FALSE;      return FALSE;
301    }    }
302    
303      /* a valid http connection may have returned an error */
304    if(request->response != 200) {    if(request->response != 200) {
305      errorf(parent, _("Download failed with code %ld:\n\n%s\n"),      errorf(parent, _("Download failed with code %ld:\n\n%s\n"),
306             request->response, http_message(request->response));             request->response, http_message(request->response));
     g_remove(request->filename);  
     request_free(request);  
307      return FALSE;      return FALSE;
308    }    }
309    
   /* memory transfer needs the result as it contains the result pointer */  
   if(request->type != NET_IO_MEM)  
     request_free(request);  
   
310    return TRUE;    return TRUE;
311  }  }
312    
313  gboolean net_io_download_file(GtkWidget *parent, char *url, char *filename) {  gboolean net_io_download_file(GtkWidget *parent, char *url, char *filename) {
   /* the request structure is shared between master and worker thread. */  
   /* typically the master thread will do some waiting until the worker */  
   /* thread returns. But the master may very well stop waiting since e.g. */  
   /* the user activated some cancel button. The client will learn this */  
   /* from the fact that it's holding the only reference to the request */  
314    net_io_request_t *request = g_new0(net_io_request_t, 1);    net_io_request_t *request = g_new0(net_io_request_t, 1);
315    
316    printf("net_io: download %s to file %s\n", url, filename);    printf("net_io: download %s to file %s\n", url, filename);
317    request->type = NET_IO_FILE;    request->type = NET_IO_DL_FILE;
318    request->url = g_strdup(url);    request->url = g_strdup(url);
319    request->filename = g_strdup(filename);    request->filename = g_strdup(filename);
320    
321    return net_io_do(parent, request);    gboolean result = net_io_do(parent, request);
322      if(!result) {
323    
324        /* remove the file that may have been written by now. the kernel */
325        /* should cope with the fact that the worker thread may still have */
326        /* an open reference to this and might thus still write to this file. */
327        /* letting the worker delete the file is worse since it may take the */
328        /* worker some time to come to the point to delete this file. If the */
329        /* user has restarted the download by then, the worker will erase that */
330        /* newly written file */
331    
332        g_remove(filename);
333      }
334    
335      request_free(request);
336      return result;
337  }  }
338    
339    
340  void *net_io_download_mem(GtkWidget *parent, char *url) {  gboolean net_io_download_mem(GtkWidget *parent, char *url, char **mem) {
   /* the request structure is shared between master and worker thread. */  
   /* typically the master thread will do some waiting until the worker */  
   /* thread returns. But the master may very well stop waiting since e.g. */  
   /* the user activated some cancel button. The client will learn this */  
   /* from the fact that it's holding the only reference to the request */  
341    net_io_request_t *request = g_new0(net_io_request_t, 1);    net_io_request_t *request = g_new0(net_io_request_t, 1);
342    
343    printf("net_io: download %s to memory\n", url);    printf("net_io: download %s to memory\n", url);
344    request->type = NET_IO_MEM;    request->type = NET_IO_DL_MEM;
345    request->url = g_strdup(url);    request->url = g_strdup(url);
346    
347    if(!net_io_do(parent, request))    gboolean result = net_io_do(parent, request);
348      return NULL;    if(result) {
349        printf("ptr = %p, len = %d\n", request->mem.ptr, request->mem.len);
350    printf("ptr = %p, len = %d\n",      *mem = request->mem.ptr;
351           request->mem.ptr, request->mem.len);    }
352    
   char *retval = request->mem.ptr;  
353    request_free(request);    request_free(request);
354    return retval;    return result;
355  }  }

Legend:
Removed from v.4  
changed lines
  Added in v.7