public function progress($progress = self::INDETERMINATE) {
        // Check we are inside a progress section.
        $max = end($this->maxes);
        if ($max === false) {
            throw new \coding_exception(
                    'progress() without start_progress');
        }

        // Check and apply new progress.
        if ($progress === self::INDETERMINATE) {
            // Indeterminate progress.
            if ($max !== self::INDETERMINATE) {
                throw new \coding_exception(
                        'progress() INDETERMINATE, expecting value');
            }
        } else {
            // Determinate progress.
            $current = end($this->currents);
            if ($max === self::INDETERMINATE) {
                throw new \coding_exception(
                        'progress() with value, expecting INDETERMINATE');
            } else if ($progress < 0 || $progress > $max) {
                throw new \coding_exception(
                        'progress() value out of range');
            } else if ($progress < $current) {
                throw new \coding_exception(
                        'progress() value may not go backwards');
            }
            $this->currents[key($this->currents)] = $progress;
        }

        // Don't update progress bar too frequently (more than once per second).
        $now = $this->get_time();
        if ($now === $this->lastprogresstime) {
            return;
        }

        // Update progress.
        $this->count++;
        $this->lastprogresstime = $now;

        // Update time limit before next progress display.
        \core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
        $this->update_progress();
    }