runtime/Worker.py
author Edouard Tisserant <edouard.tisserant@gmail.com>
Tue, 14 Apr 2020 17:15:02 +0200
branchsvghmi
changeset 2928 c73d0b042ca8
parent 2611 a1bf03277cec
child 3584 8a54fd58a552
permissions -rw-r--r--
SVGHMI: HMI:DropDown element is brought in front when opened, so that it isn't occulted by other widgets at the same level.
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     1
#!/usr/bin/env python
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     2
# -*- coding: utf-8 -*-
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     3
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     4
# This file is part of Beremiz runtime.
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     5
#
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     6
# Copyright (C) 2018: Edouard TISSERANT
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     7
#
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     8
# See COPYING.Runtime file for copyrights details.
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
     9
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    10
from __future__ import absolute_import
2307
c44692b53736 Show more exceptions on stdout, particularly those that are raised by AutoLoad (first item in Main Thread worker)
Edouard Tisserant
parents: 2271
diff changeset
    11
import sys
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    12
from threading import Lock, Condition
2492
7dd551ac2fa0 check_sources.sh makes me become even less productive
Edouard Tisserant <edouard.tisserant@gmail.com>
parents: 2486
diff changeset
    13
import six
2537
eb4a4cc41914 Fix various pylint and pep8 errors
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2536
diff changeset
    14
from six.moves import _thread
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    15
2309
d8fb90a2e11f Please pylint and pep8
Edouard Tisserant <edouard.tisserant@gmail.com>
parents: 2307
diff changeset
    16
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    17
class job(object):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    18
    """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    19
    job to be executed by a worker
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    20
    """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    21
    def __init__(self, call, *args, **kwargs):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    22
        self.job = (call, args, kwargs)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    23
        self.result = None
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
    24
        self.success = None
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    25
        self.exc_info = None
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    26
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    27
    def do(self):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    28
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    29
        do the job by executing the call, and deal with exceptions
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    30
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    31
        try:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    32
            call, args, kwargs = self.job
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    33
            self.result = call(*args, **kwargs)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    34
            self.success = True
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    35
        except Exception:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    36
            self.success = False
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    37
            self.exc_info = sys.exc_info()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    38
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    39
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    40
class worker(object):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    41
    """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    42
    serialize main thread load/unload of PLC shared objects
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    43
    """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    44
    def __init__(self):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    45
        # Only one job at a time
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    46
        self._finish = False
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    47
        self._threadID = None
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    48
        self.mutex = Lock()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    49
        self.todo = Condition(self.mutex)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    50
        self.done = Condition(self.mutex)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    51
        self.free = Condition(self.mutex)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    52
        self.job = None
2611
a1bf03277cec Fixed typo leading to some exception in some rare corner cases in worker.py
Edouard Tisserant
parents: 2604
diff changeset
    53
        self.enabled = False
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    54
2536
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    55
    def reraise(self, job):
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    56
        """
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    57
        reraise exception happend in a job
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    58
        @param job: job where original exception happend
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    59
        """
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    60
        exc_type = job.exc_info[0]
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    61
        exc_value = job.exc_info[1]
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    62
        exc_traceback = job.exc_info[2]
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    63
        six.reraise(exc_type, exc_value, exc_traceback)
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    64
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    65
    def runloop(self, *args, **kwargs):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    66
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    67
        meant to be called by worker thread (blocking)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    68
        """
2537
eb4a4cc41914 Fix various pylint and pep8 errors
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2536
diff changeset
    69
        self._threadID = _thread.get_ident()
2467
fce6ab7ae156 Enure that autostart blocks other @RunInMain PLCObject methods, and _unblocks_ them once autostart is done...
Edouard Tisserant
parents: 2463
diff changeset
    70
        self.mutex.acquire()
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
    71
        self.enabled = True
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    72
        if args or kwargs:
2307
c44692b53736 Show more exceptions on stdout, particularly those that are raised by AutoLoad (first item in Main Thread worker)
Edouard Tisserant
parents: 2271
diff changeset
    73
            _job = job(*args, **kwargs)
c44692b53736 Show more exceptions on stdout, particularly those that are raised by AutoLoad (first item in Main Thread worker)
Edouard Tisserant
parents: 2271
diff changeset
    74
            _job.do()
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
    75
            # _job.success can't be None after do()
2536
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    76
            if not _job.success:
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
    77
                self.reraise(_job)
2486
44c2a4e2b84d Fixed deadlock in runtime's Worker. Was discovered while using WAMP and PYRO simultaneously.
Edouard Tisserant
parents: 2467
diff changeset
    78
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    79
        while not self._finish:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    80
            self.todo.wait()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    81
            if self.job is not None:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    82
                self.job.do()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    83
                self.done.notify()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    84
            else:
2486
44c2a4e2b84d Fixed deadlock in runtime's Worker. Was discovered while using WAMP and PYRO simultaneously.
Edouard Tisserant
parents: 2467
diff changeset
    85
                break
44c2a4e2b84d Fixed deadlock in runtime's Worker. Was discovered while using WAMP and PYRO simultaneously.
Edouard Tisserant
parents: 2467
diff changeset
    86
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    87
        self.mutex.release()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    88
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    89
    def call(self, *args, **kwargs):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    90
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    91
        creates a job, execute it in worker thread, and deliver result.
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    92
        if job execution raise exception, re-raise same exception
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    93
        meant to be called by non-worker threads, but this is accepted.
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    94
        blocking until job done
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    95
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    96
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    97
        _job = job(*args, **kwargs)
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
    98
2537
eb4a4cc41914 Fix various pylint and pep8 errors
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2536
diff changeset
    99
        if self._threadID == _thread.get_ident():
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   100
            # if caller is worker thread execute immediately
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   101
            _job.do()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   102
        else:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   103
            # otherwise notify and wait for completion
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   104
            self.mutex.acquire()
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   105
            if not self.enabled:
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   106
                self.mutex.release()
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   107
                raise EOFError("Worker is disabled")
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   108
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   109
            while self.job is not None:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   110
                self.free.wait()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   111
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   112
            self.job = _job
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   113
            self.todo.notify()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   114
            self.done.wait()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   115
            self.job = None
2486
44c2a4e2b84d Fixed deadlock in runtime's Worker. Was discovered while using WAMP and PYRO simultaneously.
Edouard Tisserant
parents: 2467
diff changeset
   116
            self.free.notify()
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   117
            self.mutex.release()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   118
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   119
        if _job.success is None:
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   120
            raise EOFError("Worker job was interrupted")
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   121
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   122
        if _job.success:
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   123
            return _job.result
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   124
        else:
2536
2747d6e72eb8 Fix invalid python3 syntax
Andrey Skvortsov <andrej.skvortzov@gmail.com>
parents: 2492
diff changeset
   125
            self.reraise(_job)
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   126
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   127
    def quit(self):
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   128
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   129
        unblocks main thread, and terminate execution of runloop()
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   130
        """
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   131
        # mark queue
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   132
        self._finish = True
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   133
        self.mutex.acquire()
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   134
        self.enabled = False
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   135
        self.job = None
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   136
        self.todo.notify()
2604
c8a25a3a7f8b Runtime worker : unblock the last waiting job and prevent any new job to wait when Main Worker is being shut down.
Edouard Tisserant
parents: 2537
diff changeset
   137
        self.done.notify()
2270
d9175daf6522 Refactoring. Separated PLC Object, PYRO Server and MainWorker :
Edouard Tisserant
parents:
diff changeset
   138
        self.mutex.release()