def _execute_forked(self, tasks, log):
        """
        Executes the tasks in the forked process. Multiple tasks can be passed
        for batch processing. However, they must all use the same function and
        will share the execution entry.
        """
        success = False

        execution = {}

        assert len(tasks)
        task_func = tasks[0].serialized_func
        assert all([task_func == task.serialized_func for task in tasks[1:]])

        execution['time_started'] = time.time()

        exc = None
        exc_info = None

        try:
            func = tasks[0].func

            is_batch_func = getattr(func, '_task_batch', False)
            g['current_task_is_batch'] = is_batch_func

            if is_batch_func:
                # Batch process if the task supports it.
                params = [{
                    'args': task.args,
                    'kwargs': task.kwargs,
                } for task in tasks]
                task_timeouts = [task.hard_timeout for task in tasks if task.hard_timeout is not None]
                hard_timeout = ((max(task_timeouts) if task_timeouts else None)
                                or
                                getattr(func, '_task_hard_timeout', None) or
                                self.config['DEFAULT_HARD_TIMEOUT'])

                g['current_tasks'] = tasks
                with UnixSignalDeathPenalty(hard_timeout):
                    func(params)

            else:
                # Process sequentially.
                for task in tasks:
                    hard_timeout = (task.hard_timeout or
                                    getattr(func, '_task_hard_timeout', None) or
                                    self.config['DEFAULT_HARD_TIMEOUT'])

                    g['current_tasks'] = [task]
                    with UnixSignalDeathPenalty(hard_timeout):
                        func(*task.args, **task.kwargs)

        except RetryException as exc:
            execution['retry'] = True
            if exc.method:
                execution['retry_method'] = serialize_retry_method(exc.method)
            execution['log_error'] = exc.log_error
            execution['exception_name'] = serialize_func_name(exc.__class__)
            exc_info = exc.exc_info or sys.exc_info()
        except (JobTimeoutException, Exception) as exc:
            execution['exception_name'] = serialize_func_name(exc.__class__)
            exc_info = sys.exc_info()
        else:
            success = True

        if not success:
            execution['time_failed'] = time.time()
            if self.store_tracebacks:
                # Currently we only log failed task executions to Redis.
                execution['traceback'] = \
                        ''.join(traceback.format_exception(*exc_info))
            execution['success'] = success
            execution['host'] = socket.gethostname()
            serialized_execution = json.dumps(execution)
            for task in tasks:
                self.connection.rpush(self._key('task', task.id, 'executions'),
                                      serialized_execution)

        return success
Executes the tasks in the forked process. Multiple tasks can be passed
        for batch processing. However, they must all use the same function and
        will share the execution entry.