def set_status(self, status, msg):
        """
        Set and return the status of the task.

        Args:
            status: Status object or string representation of the status
            msg: string with human-readable message used in the case of errors.
        """
        # truncate string if it's long. msg will be logged in the object and we don't want to waste memory.
        if len(msg) > 2000:
            msg = msg[:2000]
            msg += "\n... snip ...\n"

        # Locked files must be explicitly unlocked
        if self.status == self.S_LOCKED or status == self.S_LOCKED:
            err_msg = (
                 "Locked files must be explicitly unlocked before calling set_status but\n"
                 "task.status = %s, input status = %s" % (self.status, status))
            raise RuntimeError(err_msg)

        status = Status.as_status(status)

        changed = True
        if hasattr(self, "_status"):
            changed = (status != self._status)

        self._status = status

        if status == self.S_RUN:
            # Set datetimes.start when the task enters S_RUN
            if self.datetimes.start is None:
                self.datetimes.start = datetime.datetime.now()

        # Add new entry to history only if the status has changed.
        if changed:
            if status == self.S_SUB:
                self.datetimes.submission = datetime.datetime.now()
                self.history.info("Submitted with MPI=%s, Omp=%s, Memproc=%.1f [Gb] %s " % (
                    self.mpi_procs, self.omp_threads, self.mem_per_proc.to("Gb"), msg))

            elif status == self.S_OK:
                self.history.info("Task completed %s", msg)

            elif status == self.S_ABICRITICAL:
                self.history.info("Status set to S_ABI_CRITICAL due to: %s", msg)

            else:
                self.history.info("Status changed to %s. msg: %s", status, msg)

        #######################################################
        # The section belows contains callbacks that should not
        # be executed if we are in spectator_mode
        #######################################################
        if status == self.S_DONE:
            # Execute the callback
            self._on_done()

        if status == self.S_OK:
            # Finalize the task.
            if not self.finalized:
                self._on_ok()

                # here we remove the output files of the task and of its parents.
                if self.gc is not None and self.gc.policy == "task":
                    self.clean_output_files()

            if self.status == self.S_OK:
                # Because _on_ok might have changed the status.
                self.send_signal(self.S_OK)

        return status