source: rtems/cpukit/include/rtems/score/watchdogimpl.h @ a660e9dc

Last change on this file since a660e9dc was a660e9dc, checked in by Sebastian Huber <sebastian.huber@…>, on 09/08/22 at 08:37:05

Do not use RTEMS_INLINE_ROUTINE

Directly use "static inline" which is available in C99 and later. This brings
the RTEMS implementation closer to standard C.

Close #3935.

  • Property mode set to 100644
File size: 18.5 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @ingroup RTEMSScoreWatchdog
7 *
8 * @brief This header file provides interfaces of the
9 *   @ref RTEMSScoreWatchdog which are only used by the implementation.
10 */
11
12/*
13 *  COPYRIGHT (c) 1989-2004.
14 *  On-Line Applications Research Corporation (OAR).
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 *    notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 *    notice, this list of conditions and the following disclaimer in the
23 *    documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifndef _RTEMS_SCORE_WATCHDOGIMPL_H
39#define _RTEMS_SCORE_WATCHDOGIMPL_H
40
41#include <rtems/score/watchdog.h>
42#include <rtems/score/watchdogticks.h>
43#include <rtems/score/assert.h>
44#include <rtems/score/isrlock.h>
45#include <rtems/score/percpu.h>
46#include <rtems/score/rbtreeimpl.h>
47
48#include <sys/types.h>
49#include <sys/timespec.h>
50
51#ifdef __cplusplus
52extern "C" {
53#endif
54
55/**
56 * @addtogroup RTEMSScoreWatchdog
57 *
58 * @{
59 */
60
61/**
62 * @brief Watchdog states.
63 */
64typedef enum {
65  /**
66   * @brief The watchdog is scheduled and a black node in the red-black tree.
67   */
68  WATCHDOG_SCHEDULED_BLACK,
69
70  /**
71   * @brief The watchdog is scheduled and a red node in the red-black tree.
72   */
73  WATCHDOG_SCHEDULED_RED,
74
75  /**
76   * @brief The watchdog is inactive.
77   */
78  WATCHDOG_INACTIVE,
79
80  /**
81   * @brief The watchdog is on a chain of pending watchdogs.
82   *
83   * This state is used by the timer server for example.
84   */
85  WATCHDOG_PENDING
86} Watchdog_State;
87
88/**
89 * @brief Watchdog initializer for static initialization.
90 *
91 * The processor of this watchdog is set to processor with index zero.
92 *
93 * @see _Watchdog_Preinitialize().
94 */
95#if defined(RTEMS_SMP)
96  #define WATCHDOG_INITIALIZER( routine ) \
97    { \
98      { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \
99      &_Per_CPU_Information[ 0 ].per_cpu, \
100      ( routine ), \
101      0 \
102    }
103#else
104  #define WATCHDOG_INITIALIZER( routine ) \
105    { \
106      { { { NULL, NULL, NULL, WATCHDOG_INACTIVE } } }, \
107      ( routine ), \
108      0 \
109    }
110#endif
111
112/**
113 * @brief Initializes the watchdog header.
114 *
115 * @param[out] header The header to initialize.
116 */
117static inline void _Watchdog_Header_initialize(
118  Watchdog_Header *header
119)
120{
121  _RBTree_Initialize_empty( &header->Watchdogs );
122  header->first = NULL;
123}
124
125/**
126 * @brief Returns the first of the watchdog header.
127 *
128 * @param header The watchdog header to remove the first of.
129 *
130 * @return The first of @a header.
131 */
132static inline Watchdog_Control *_Watchdog_Header_first(
133  const Watchdog_Header *header
134)
135{
136  return (Watchdog_Control *) header->first;
137}
138
139/**
140 * @brief Destroys the watchdog header.
141 *
142 * @param header The watchdog header to destroy.
143 */
144static inline void _Watchdog_Header_destroy(
145  Watchdog_Header *header
146)
147{
148  /* Do nothing */
149  (void) header;
150}
151
152/**
153 * @brief Performs a watchdog tick.
154 *
155 * @param cpu The processor for this watchdog tick.
156 */
157void _Watchdog_Tick( struct Per_CPU_Control *cpu );
158
159/**
160 * @brief Gets the state of the watchdog.
161 *
162 * @param the_watchdog The watchdog to get the state of.
163 *
164 * @return The RB_COLOR of @a the_watchdog.
165 */
166static inline Watchdog_State _Watchdog_Get_state(
167  const Watchdog_Control *the_watchdog
168)
169{
170  return (Watchdog_State) RB_COLOR( &the_watchdog->Node.RBTree, Node );
171}
172
173/**
174 * @brief Sets the state of the watchdog.
175 *
176 * @param[out] the_watchdog The watchdog to set the state of.
177 * @param state The state to set the watchdog to.
178 */
179static inline void _Watchdog_Set_state(
180  Watchdog_Control *the_watchdog,
181  Watchdog_State    state
182)
183{
184  RB_COLOR( &the_watchdog->Node.RBTree, Node ) = state;
185}
186
187/**
188 * @brief Gets the watchdog's cpu.
189 *
190 * @param the_watchdog The watchdog to get the cpu of.
191 *
192 * @return The cpu of the watchdog.
193 */
194static inline Per_CPU_Control *_Watchdog_Get_CPU(
195  const Watchdog_Control *the_watchdog
196)
197{
198#if defined(RTEMS_SMP)
199  return the_watchdog->cpu;
200#else
201  return _Per_CPU_Get_by_index( 0 );
202#endif
203}
204
205/**
206 * @brief Sets the cpu for the watchdog.
207 *
208 * @param[out] the_watchdog The watchdog to set the cpu of.
209 * @param cpu The cpu to be set as @a the_watchdog's cpu.
210 */
211static inline void _Watchdog_Set_CPU(
212  Watchdog_Control *the_watchdog,
213  Per_CPU_Control  *cpu
214)
215{
216#if defined(RTEMS_SMP)
217  the_watchdog->cpu = cpu;
218#else
219  (void) cpu;
220#endif
221}
222
223/**
224 * @brief Pre-initializes a watchdog.
225 *
226 * This routine must be called before a watchdog is used in any way.  The
227 * exception are statically initialized watchdogs via WATCHDOG_INITIALIZER().
228 *
229 * @param[out] the_watchdog The uninitialized watchdog.
230 */
231static inline void _Watchdog_Preinitialize(
232  Watchdog_Control *the_watchdog,
233  Per_CPU_Control  *cpu
234)
235{
236  _Watchdog_Set_CPU( the_watchdog, cpu );
237  _Watchdog_Set_state( the_watchdog, WATCHDOG_INACTIVE );
238
239#if defined(RTEMS_DEBUG)
240  the_watchdog->routine = NULL;
241  the_watchdog->expire = 0;
242#endif
243}
244
245/**
246 * @brief Initializes a watchdog with a new service routine.
247 *
248 * The watchdog must be inactive.
249 *
250 * @param[out] the_watchdog The watchdog to initialize.
251 * @param routing The service routine for @a the_watchdog.
252 */
253static inline void _Watchdog_Initialize(
254  Watchdog_Control               *the_watchdog,
255  Watchdog_Service_routine_entry  routine
256)
257{
258  _Assert( _Watchdog_Get_state( the_watchdog ) == WATCHDOG_INACTIVE );
259  the_watchdog->routine = routine;
260}
261
262/**
263 * @brief Calls the routine of each not expired watchdog control node.
264 *
265 * @param header The watchdog header.
266 * @param first The first watchdog control node.
267 * @param now The current time to check the expiration time against.
268 * @param lock The lock that is released before calling the routine and then
269 *      acquired after the call.
270 * @param lock_context The lock context for the release before calling the
271 *      routine and for the acquire after.
272 */
273void _Watchdog_Do_tickle(
274  Watchdog_Header  *header,
275  Watchdog_Control *first,
276  uint64_t          now,
277#if defined(RTEMS_SMP)
278  ISR_lock_Control *lock,
279#endif
280  ISR_lock_Context *lock_context
281);
282
283#if defined(RTEMS_SMP)
284  #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \
285    _Watchdog_Do_tickle( header, first, now, lock, lock_context )
286#else
287  #define _Watchdog_Tickle( header, first, now, lock, lock_context ) \
288    _Watchdog_Do_tickle( header, first, now, lock_context )
289#endif
290
291/**
292 * @brief Inserts a watchdog into the set of scheduled watchdogs according to
293 * the specified expiration time.
294 *
295 * The watchdog must be inactive.
296 *
297 * @param[in, out] header The set of scheduler watchdogs to insert into.
298 * @param[in, out] the_watchdog The watchdog to insert.
299 * @param expire The expiration time for the watchdog.
300 */
301void _Watchdog_Insert(
302  Watchdog_Header  *header,
303  Watchdog_Control *the_watchdog,
304  uint64_t          expire
305);
306
307/**
308 * @brief In the case the watchdog is scheduled, then it is removed from the set of
309 * scheduled watchdogs.
310 *
311 * The watchdog must be initialized before this call.
312 *
313 * @param[in, out] header The scheduled watchdogs.
314 * @param[in, out] the_watchdog The watchdog to remove.
315 */
316void _Watchdog_Remove(
317  Watchdog_Header  *header,
318  Watchdog_Control *the_watchdog
319);
320
321/**
322 * @brief In the case the watchdog is scheduled, then it is removed from the set of
323 * scheduled watchdogs.
324 *
325 * The watchdog must be initialized before this call.
326 *
327 * @param[in, out] header The scheduled watchdogs.
328 * @param[in, out] the_watchdog The watchdog to remove.
329 * @param now The current time.
330 *
331 * @retval other The difference of the now and expiration time.
332 * @retval 0 The now time is greater than or equal to the expiration time of
333 * the watchdog.
334 */
335static inline uint64_t _Watchdog_Cancel(
336  Watchdog_Header  *header,
337  Watchdog_Control *the_watchdog,
338  uint64_t          now
339)
340{
341  uint64_t expire;
342  uint64_t remaining;
343
344  expire = the_watchdog->expire;
345
346  if ( now < expire ) {
347    remaining = expire - now;
348  } else {
349    remaining = 0;
350  }
351
352  _Watchdog_Remove( header, the_watchdog );
353
354  return remaining;
355}
356
357/**
358 * @brief Checks if the watchdog is scheduled.
359 *
360 * @param the_watchdog The watchdog for the verification.
361 *
362 * @retval true The watchdog is scheduled.
363 * @retval false The watchdog is inactive.
364 */
365static inline bool _Watchdog_Is_scheduled(
366  const Watchdog_Control *the_watchdog
367)
368{
369  return _Watchdog_Get_state( the_watchdog ) < WATCHDOG_INACTIVE;
370}
371
372/**
373 * @brief Sets the first watchdog of the watchdog collection to the next
374 * watchdog of the current first watchdog.
375 *
376 * This function may be used during watchdog removals, see _Watchdog_Remove()
377 * and _Watchdog_Tickle().
378 *
379 * @param[in, out] header is the watchdog collection header.
380 *
381 * @param first is the current first watchdog which should be removed
382 *   afterwards.
383 */
384static inline void _Watchdog_Next_first(
385  Watchdog_Header        *header,
386  const Watchdog_Control *first
387)
388{
389  RBTree_Node *right;
390
391  /*
392   * This function uses the following properties of red-black trees:
393   *
394   * 1. Every leaf (NULL) is black.
395   *
396   * 2. If a node is red, then both its children are black.
397   *
398   * 3. Every simple path from a node to a descendant leaf contains the same
399   *    number of black nodes.
400   *
401   * The first node has no left child.  So every path from the first node has
402   * exactly one black node (including leafs).  The first node cannot have a
403   * non-leaf black right child.  It may have a red right child.  In this case
404   * both children must be leafs.
405   */
406  _Assert( header->first == &first->Node.RBTree );
407  _Assert( _RBTree_Left( &first->Node.RBTree ) == NULL );
408  right = _RBTree_Right( &first->Node.RBTree );
409
410  if ( right != NULL ) {
411    _Assert( RB_COLOR( right, Node ) == RB_RED );
412    _Assert( _RBTree_Left( right ) == NULL );
413    _Assert( _RBTree_Right( right ) == NULL );
414    header->first = right;
415  } else {
416    header->first = _RBTree_Parent( &first->Node.RBTree );
417  }
418}
419
420/**
421 * @brief The maximum watchdog ticks value for the far future.
422 */
423#define WATCHDOG_MAXIMUM_TICKS UINT64_MAX
424
425#define WATCHDOG_NANOSECONDS_PER_SECOND 1000000000
426
427/**
428 * @brief The bits necessary to store 1000000000
429 * (= WATCHDOG_NANOSECONDS_PER_SECOND) nanoseconds.
430 *
431 * The expiration time is an unsigned 64-bit integer.  To store nanoseconds
432 * timeouts we use 30 bits (2**30 == 1073741824) for the nanoseconds and 34
433 * bits for the seconds since UNIX Epoch.  This leads to a year 2514 problem.
434 */
435#define WATCHDOG_BITS_FOR_1E9_NANOSECONDS 30
436
437/**
438 * @brief The maximum number of seconds representable in the nanoseconds
439 * watchdog format.
440 *
441 * We have 2**34 bits for the seconds part.
442 */
443#define WATCHDOG_MAX_SECONDS 0x3ffffffff
444
445/**
446 * @brief Checks if the timespec is a valid timespec for a watchdog.
447 *
448 * @param ts The timespec for the verification.
449 *
450 * @retval true The timespec is a valid timespec.
451 * @retval false The timespec is invalid.
452 */
453static inline bool _Watchdog_Is_valid_timespec(
454  const struct timespec *ts
455)
456{
457  return ts != NULL
458    && (unsigned long) ts->tv_nsec < WATCHDOG_NANOSECONDS_PER_SECOND;
459}
460
461/**
462 * @brief Checks if the timespec is a valid interval timespec for a watchdog.
463 *
464 * @param ts The timespec for the verification.
465 *
466 * @retval true The timespec is a valid interval timespec.
467 * @retval false The timespec is invalid.
468 */
469static inline bool _Watchdog_Is_valid_interval_timespec(
470  const struct timespec *ts
471)
472{
473  return _Watchdog_Is_valid_timespec( ts ) && ts->tv_sec >= 0;
474}
475
476/**
477 * @brief Adds the delta timespec to the current time if the delta is a valid
478 * interval timespec.
479 *
480 * @param[in, out] now The current time.
481 * @param delta The delta timespec for the addition.
482 *
483 * @retval pointer Pointer to the now timespec.
484 * @retval NULL @a delta is not a valid interval timespec.
485 */
486static inline const struct timespec * _Watchdog_Future_timespec(
487  struct timespec       *now,
488  const struct timespec *delta
489)
490{
491  uint64_t sec;
492
493  if ( !_Watchdog_Is_valid_interval_timespec( delta ) ) {
494    return NULL;
495  }
496
497  sec = (uint64_t) now->tv_sec;
498  sec += (uint64_t) delta->tv_sec;
499  now->tv_nsec += delta->tv_nsec;
500
501  /* We have 2 * (2**63 - 1) + 1 == UINT64_MAX */
502  if ( now->tv_nsec >= WATCHDOG_NANOSECONDS_PER_SECOND ) {
503    now->tv_nsec -= WATCHDOG_NANOSECONDS_PER_SECOND;
504    ++sec;
505  }
506
507  if ( sec <= INT64_MAX ) {
508    now->tv_sec = sec;
509  } else {
510    now->tv_sec = INT64_MAX;
511  }
512
513  return now;
514}
515
516/**
517 * @brief Checks if the timespec is too far in the future.
518 *
519 * @param ts The timespec for the verification.
520 *
521 * @retval true @a ts is too far in the future.
522 * @retval false @a ts is not too far in the future.
523 */
524static inline bool _Watchdog_Is_far_future_timespec(
525  const struct timespec *ts
526)
527{
528  return ts->tv_sec > WATCHDOG_MAX_SECONDS;
529}
530
531/**
532 * @brief Converts the seconds to ticks.
533 *
534 * @param seconds The seconds to convert to ticks.
535 *
536 * @return @a seconds converted to ticks.
537 */
538static inline uint64_t _Watchdog_Ticks_from_seconds(
539  uint32_t seconds
540)
541{
542  uint64_t ticks = seconds;
543
544  ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
545
546  return ticks;
547}
548
549/**
550 * @brief Converts the timespec in ticks.
551 *
552 * @param ts The timespec to convert to ticks.
553 *
554 * @return @a ts converted to ticks.
555 */
556static inline uint64_t _Watchdog_Ticks_from_timespec(
557  const struct timespec *ts
558)
559{
560  uint64_t ticks;
561
562  _Assert( _Watchdog_Is_valid_timespec( ts ) );
563  _Assert( ts->tv_sec >= 0 );
564  _Assert( !_Watchdog_Is_far_future_timespec( ts ) );
565
566  ticks = (uint64_t) ts->tv_sec;
567  ticks <<= WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
568  ticks |= (uint32_t) ts->tv_nsec;
569
570  return ticks;
571}
572
573/**
574 * @brief Converts the ticks to timespec.
575 *
576 * @param ticks are the ticks to convert.
577 *
578 * @param[out] ts is the timespec to return the converted ticks.
579 */
580static inline void _Watchdog_Ticks_to_timespec(
581  uint64_t         ticks,
582  struct timespec *ts
583)
584{
585  ts->tv_sec = ticks >> WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
586  ts->tv_nsec = ticks & ( ( 1U << WATCHDOG_BITS_FOR_1E9_NANOSECONDS ) - 1 );
587}
588
589/**
590 * @brief Converts the sbintime in ticks.
591 *
592 * @param sbt The sbintime to convert to ticks.
593 *
594 * @return @a sbt converted to ticks.
595 */
596static inline uint64_t _Watchdog_Ticks_from_sbintime( int64_t sbt )
597{
598  uint64_t ticks = ( sbt >> 32 ) << WATCHDOG_BITS_FOR_1E9_NANOSECONDS;
599
600  ticks |= ( (uint64_t) 1000000000 * (uint32_t) sbt ) >> 32;
601
602  return ticks;
603}
604
605/**
606 * @brief Acquires the per cpu watchdog lock in a critical section.
607 *
608 * @param cpu The cpu to acquire the watchdog lock of.
609 * @param lock_context The lock context.
610 */
611static inline void _Watchdog_Per_CPU_acquire_critical(
612  Per_CPU_Control  *cpu,
613  ISR_lock_Context *lock_context
614)
615{
616  _ISR_lock_Acquire( &cpu->Watchdog.Lock, lock_context );
617}
618
619/**
620 * @brief Releases the per cpu watchdog lock in a critical section.
621 *
622 * @param cpu The cpu to release the watchdog lock of.
623 * @param lock_context The lock context.
624 */
625static inline void _Watchdog_Per_CPU_release_critical(
626  Per_CPU_Control  *cpu,
627  ISR_lock_Context *lock_context
628)
629{
630  _ISR_lock_Release( &cpu->Watchdog.Lock, lock_context );
631}
632
633/**
634 * @brief Sets the watchdog's cpu to the given instance and sets its expiration
635 *      time to the watchdog expiration time of the cpu plus the ticks.
636 *
637 * @param[in, out] the_watchdog The watchdog to set the cpu and expiration time of.
638 * @param cpu The cpu for the watchdog.
639 * @param ticks The ticks to add to the expiration time.
640 *
641 * @return The new expiration time of the watchdog.
642 */
643static inline uint64_t _Watchdog_Per_CPU_insert_ticks(
644  Watchdog_Control  *the_watchdog,
645  Per_CPU_Control   *cpu,
646  Watchdog_Interval  ticks
647)
648{
649  ISR_lock_Context  lock_context;
650  Watchdog_Header  *header;
651  uint64_t          expire;
652
653  header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ];
654
655  _Watchdog_Set_CPU( the_watchdog, cpu );
656
657  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
658  expire = ticks + cpu->Watchdog.ticks;
659  _Watchdog_Insert(header, the_watchdog, expire);
660  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
661  return expire;
662}
663
664/**
665 * @brief Sets the watchdog's cpu and inserts it with the given expiration time
666 *      in the scheduled watchdogs.
667 *
668 * @param[in, out] the_watchdog The watchdog to set cpu and expiration time of.
669 * @param cpu The cpu for the operation.
670 * @param[in, out] header The scheduled watchdogs.
671 * @param expire The expiration time for the watchdog.
672 *
673 * @return The expiration time of the watchdog.
674 */
675static inline uint64_t _Watchdog_Per_CPU_insert(
676  Watchdog_Control *the_watchdog,
677  Per_CPU_Control  *cpu,
678  Watchdog_Header  *header,
679  uint64_t          expire
680)
681{
682  ISR_lock_Context lock_context;
683
684  _Watchdog_Set_CPU( the_watchdog, cpu );
685
686  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
687  _Watchdog_Insert( header, the_watchdog, expire );
688  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
689  return expire;
690}
691
692/**
693 * @brief Removes the watchdog from the cpu and the scheduled watchdogs.
694 *
695 * @param[in, out] the_watchdog The watchdog to remove.
696 * @param cpu The cpu to remove the watchdog from.
697 * @param[in, out] The scheduled watchdogs.
698 */
699static inline void _Watchdog_Per_CPU_remove(
700  Watchdog_Control *the_watchdog,
701  Per_CPU_Control  *cpu,
702  Watchdog_Header  *header
703)
704{
705  ISR_lock_Context lock_context;
706
707  _Watchdog_Per_CPU_acquire_critical( cpu, &lock_context );
708  _Watchdog_Remove(
709    header,
710    the_watchdog
711  );
712  _Watchdog_Per_CPU_release_critical( cpu, &lock_context );
713}
714
715/**
716 * @brief Removes the watchdog from the cpu and the scheduled watchdogs.
717 *
718 * @param[in, out] the_watchdog The watchdog to remove.
719 */
720static inline void _Watchdog_Per_CPU_remove_ticks(
721  Watchdog_Control *the_watchdog
722)
723{
724  Per_CPU_Control *cpu;
725
726  cpu = _Watchdog_Get_CPU( the_watchdog );
727  _Watchdog_Per_CPU_remove(
728    the_watchdog,
729    cpu,
730    &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ]
731  );
732}
733
734/** @} */
735
736#ifdef __cplusplus
737}
738#endif
739
740#endif
741/* end of include file */
Note: See TracBrowser for help on using the repository browser.